<?php
// +----------------------------------------------------------------------
// | 基础类型服务层
// +----------------------------------------------------------------------
// | 主要服务于任务相关数据操作
// +----------------------------------------------------------------------
// | 错误编码头 201xxx
// +----------------------------------------------------------------------

namespace common\service;

use common\exception\LogicException;
use common\model\AttributeModel;
use common\model\CheckListModel;
use common\model\CosMediaModel;
use common\model\EntityModel;
use common\model\MediaModel;
use common\model\ModuleModel;
use common\model\OrderTaskListModel;
use common\model\OrderTaskUpstreamVersionModel;
use common\model\OriginalPlanNodeDetailModel;
use common\model\PlanModel;
use common\model\PlaylistItemVersionModel;
use common\model\ProjectMemberModel;
use common\model\ProjectModel;
use common\model\ProjectTemplateModel;
use common\model\ReviewRequestNodeModel;
use common\model\StatusModel;
use common\model\StepCategoryModel;
use common\model\StepModel;
use common\model\StepProjectModel;
use common\model\StepWorkflowModel;
use common\model\TagModel;
use common\model\TaskExecutorRecordModel;
use common\model\TaskModel;
use common\model\TaskProcessFileModel;
use common\model\TaskRelationModel;
use common\model\TimelogModel;
use common\model\UserModel;
use common\model\VersionCheckListModel;
use common\model\VersionModel;
use support\EntityTaskFilterParser;
use support\ErrorCode;
use support\SingletonTrait;
use think\Exception;
use think\Hook;
use think\module\Module;
use Throwable;
use Webman\Stomp\Client;

use function tools\objectToArray;


class TaskService
{

    const HEADMAN_CODE = "headman";//组长
    const ARTILIST_CODE = "artist";//艺术家
    const PRODUCER_CODE = "producer";//制片人
    const DIRECTOR_CODE = "director";//导演
    const QA_CODE = 'quality_assurance'; //QA
    const EXECUTIVE_PRODUCER_CODE = "executive_producer";// 生产制片
    const STEP_PRODUCER_CODE = "step_producer";// 环节制片
    const MOBILE_CATCHER_CODE = "mobile_catcher";// 捕捉师
    const WRITERS_CODE = "writers";// 编剧
    const CREATIVE_CODE = "creative";// 创意
    const BUSINESS_PRODUCER_CODE = 'business_producer'; // 商务制片

    protected $taskModel;
    protected $mediaModel;

    // 实体任务筛选条件解析工具类
    use EntityTaskFilterParser;

    // 单例工具
    use SingletonTrait;

    public function __construct()
    {
        $this->mediaModel = model(MediaModel::class);
        $this->taskModel = model(TaskModel::class);
    }


    /**
     * 获取拆分资产数据
     * @param $entity_id
     * @return array|mixed
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getAssetDataList($entity_id)
    {
        // shot_animation 镜头动画
        // asset 资产
        $taskData = model(TaskModel::class)
            ->field(['json'])
            ->where(['entity_id' => $entity_id, 'code' => 'shot_animation'])
            ->find();

        if (empty($taskData['json'])) {
            return [];
        }

        $taskData = json_decode($taskData['json'], true);

        //检查reference_asset 是不是字符串,如果不是跳过
        if (!empty($taskData['reference_asset']) && is_string($taskData['reference_asset'])) {
            $taskData['reference_asset'] = json_decode($taskData['reference_asset'], true);
        }

        return $taskData;
    }

    /**
     * 获取镜头实体参数
     * @return void
     *
     */
    public function getModuleByName()
    {
        return model(ModuleModel::class)
            ->where(['code' => 'shot'])
            ->field(['id', 'type', 'active', 'name', 'code', 'uuid'])
            ->find();
    }

    /**
     * 批量指派任务给组长
     * @param $params
     * @param $assigneeUserId
     * @return array
     * @throws Throwable
     */
    public function setStepTaskAssigneeBatch($params, $assigneeUserId)
    {
        $res = [];
        $model = model(TaskModel::class);
        $model->startTrans();
        try {
            foreach ($params as $task) {
                $res[$task['executor_id']][] = $this->setStepTaskAssignee($task['task_id'], $task['executor_id'], $assigneeUserId);
            }
            $model->commit();
        } catch (Throwable $e) {
            $model->rollback();
            throw $e;
        }

        // 通知任务执行人
//        try {
//            $messageService = new MessageService();
//            $messageService->notifyInsideSupplier($res, $assigneeUserId);
//        } catch (\Throwable $e) {
//            trace(format_exception_info($e), "ERR");
//        }

        return $res;
    }

    /**
     * 组长指派工序任务
     * @update 2021年7月21日 执行人用户id调整为saas user_id
     * @param $params
     * @param $userId
     * @param $tenantId
     * @return array
     * @throws Throwable
     */
    public function setStepTaskExecutorBatch($params, $userId, $tenantId): array
    {
        $res = [];

        if (empty($params)) {
            return $res;
        }

        $firstTask = $this->taskModel->where(['id' => $params[0]['task_id']])->field('project_id,step_category_id')->find();
        $projectId = $firstTask['project_id'];

        $projectMemberData = [
            'user_list' => [],
            'project_id' => $projectId,
        ];
        $assignUserList = [];

        $this->taskModel->startTrans();
        try {
            foreach ($params as $task) {
                $res[] = $this->setStepTaskExecutor($task['task_id'], $userId, $task['executor_id'], $tenantId, 'inside');
                if ($task['executor_id'] > 0) {
                    $assignUserList[$task['executor_id']] = ['executor_id' => $task['executor_id'], 'task_id' => $task['task_id']];
                }

                // 更换执行人,暂停正在执行的 TimeLog
                $timeLog = model(TimelogModel::class)->findData([
                    'filter' => [
                        'module_id' => Module::$moduleDictData['module_index_by_code']['task']['id'],
                        'link_id' => $task['task_id'],
                        'complete' => 'no',
                    ],
                    'order' => 'id DESC',
                ]);
                if (!empty($timeLog)) {
                    // 暂停当前任务正在执行的timeLog
                    $paramStop = [
                        'id' => $timeLog['id'],
                    ];
                    TimelogService::getInstance()->stopTimer($paramStop);
                }
            }

            // 查询当前任务环节信息
            $taskStepCategoryMap = [];
            $taskIds = array_column($res, 'id');
            $taskStepCategoryData = model(TaskModel::class)
                ->where(['task.id' => ['IN', $taskIds]])
                ->field('task.id as task_id,task.step_category_id')->select();

            if (!empty($taskStepCategoryData)) {
                $taskStepCategoryMap = array_column($taskStepCategoryData, null, 'task_id');
            }

            // 通过项目id找到项目模板配置的工序
            $showProducerStepCategoryConfigArrayGroup = ProjectService::getInstance()->getProducerStepCategoryConfig($projectId);

            if (!empty($assignUserList)) {
                $roleFindRequestData = ['param' => ['filter' => ['code' => TaskService::ARTILIST_CODE]]];
                $artistRoleData = teamones_request('teamones-im', 'role/find', $roleFindRequestData)['data'];
                $projectMemberData['user_list'] = array_map(function ($item) use ($taskStepCategoryMap, $showProducerStepCategoryConfigArrayGroup, $artistRoleData) {

                    $stepCategoryIdArr = [];
                    if (!empty($showProducerStepCategoryConfigArrayGroup)) {
                        foreach ($showProducerStepCategoryConfigArrayGroup as $showProducerStepCategoryConfigArrayItem) {

                            $stepCategoryId = $taskStepCategoryMap[$item['task_id']]['step_category_id'] ?? null;
                            if (in_array($stepCategoryId, array_column($showProducerStepCategoryConfigArrayItem, 'id'))) {
                                $stepCategoryIdArr = $showProducerStepCategoryConfigArrayItem;
                                break;
                            }
                        }
                    }

                    $stepCategoryIdArr = array_column($stepCategoryIdArr, 'id');
                    sort($stepCategoryIdArr);
                    return [
                        'role_code' => TaskService::ARTILIST_CODE,
                        'role_id' => $artistRoleData['id'] ?? 0,
                        'user_id' => $item['executor_id'],
                        'type' => ProjectMemberModel::PRODUCTION_TYPE,
                        'step_category_ids' => join(',', $stepCategoryIdArr),
                    ];
                }, $assignUserList);
                // 项目成员添加
                $projectService = ProjectService::getInstance();
                $projectService->batchCreateProjectMemberMultiRole($projectMemberData);
            }

            $this->taskModel->commit();
        } catch (Throwable $e) {
            $this->taskModel->rollback();
            throw $e;
        }

        // 通知任务执行人
//        try {
//        $messageService = new MessageService();
//        $messageService->notifyStepTaskExecutor($res);
//        } catch (\Throwable $e) {
//            trace(format_exception_info($e), "ERR");
//        }

        return $res;
    }

    /**
     * 获得一颗任务树
     * 集 =>实体 =>任务  tree
     * @param $filter
     * @param string $moduleCode
     * @param $page
     * @param bool $onlyTotal
     * @return array
     */
    public function getEntityTaskTree($filter, string $moduleCode, $page, bool $onlyTotal)
    {
        $baseFilter = [];
        $this->keepFixFieldsFilter($filter, 'entity', $baseFilter);
        $projectId = $filter['project_id'];
        $planService = PlanService::getInstance();
        $stepCategoryPlans = $planService->getProjectStepCategoryPlan($projectId, $moduleCode);
        $stepCategoryPlansMap = array_column($stepCategoryPlans, null, 'step_category_code');
        $project = model(ProjectModel::class)->field('id,name,code,start_time,end_time,delivery_time,tenant_id')->find($projectId);
        if (empty($project)) {
            throw new LogicException('missing project', ErrorCode::MISSING_PROJECT_ID_PARAM);
        }
        $projectTenantId = $project['tenant_id'];
        $episodeWhere = [
            'module_id' => module_by_code('episode')['id'],
            'project_id' => $projectId,
        ];
        if (!empty($filter['episode_id'])) {
            $episodeWhere['id'] = $filter['episode_id'];
            $episode = model(EntityModel::class)
                ->where($episodeWhere)
                ->field("id,name,code,module_id,start_time,end_time,duration")
                ->find();
            // 集筛选
            if (!empty($episode)) {
                $entityFilter[$moduleCode . '.initial_episode_code'] = $episode['code'];
            }
        }
        $entityFilter = array_merge($baseFilter, [
            $moduleCode . '.module_id' => module_by_code($moduleCode)['id'],
            $moduleCode . '.project_id' => $projectId,
            $moduleCode . '.is_generate' => 'yes',
        ]);
        if (empty($entityFilter[$moduleCode . '.name'])) {
            unset($entityFilter[$moduleCode . '.name']);
        }
        // 格式化filter
        $entityFilter = parse_filter_to_tp_where($entityFilter, $moduleCode);
        if ($onlyTotal) {
            //不显示任务  那么就查询所有 生成任务的实体
            $total = model(EntityModel::class)
                ->alias($moduleCode)
                ->where($entityFilter)
                ->count();
            return success_response('OK', ['total' => $total]);
        }

        $entityQueryFields = [
            "{$moduleCode}.id as eid",
            "{$moduleCode}.name",
            "{$moduleCode}.code",
            "{$moduleCode}.duration AS e_duration",
            "{$moduleCode}.initial_episode_code",
            "{$moduleCode}.module_id",
            "{$moduleCode}.entity_id",
            "{$moduleCode}.entity_module_id",
            "{$moduleCode}.json as e_json",
            "{$moduleCode}.is_assignee as e_is_assignee",
            "{$moduleCode}.initial_episode_code as e_initial_episode_code",
            "{$moduleCode}.plan_start_time as start_time",
            "{$moduleCode}.plan_end_time as end_time",
            "JSON_UNQUOTE(JSON_EXTRACT({$moduleCode}.json,'$.storyboard_sequence')) AS storyboard_sequence",
        ];

        $taskQueryFields = [
            "task.id as tid",
            "task.entity_id",
            "task.name",
            "task.code as t_code",
            "task.type as t_type",
            "task.plan_start_time as t_start_time",
            "task.plan_end_time as t_end_time",
            "task.plan_duration as t_duration",
            "task.executor as executor",
            "task.executor_tenant_id as executor_tenant_id",
            "task.assignee as assignee",
            "task.step_category_id as step_category_id",
            "task.pre_task_id as pre_task_id",
            "task.department_id",
            "JSON_UNQUOTE(JSON_EXTRACT(task.`json`,'$.task_status')) as task_status_id",
            "step_category.parent_id as parent_step_category_id",
            "step_category.index as step_category_index",
        ];
        // 先查询所有的实体 然后处理任务数据
        $entityList = model(EntityModel::class)->alias($moduleCode)
            ->where($entityFilter)
            ->page($page[0], $page[1])
            ->field($entityQueryFields)
            ->order("{$moduleCode}.initial_episode_code,{$moduleCode}.code")
            ->select();
        $taskFilter = [
            'task.project_id' => $projectId,
            'task.step_type' => 'step',
            'task.entity_id' => ['IN', array_column($entityList, 'eid')],
        ];
        $tasks = model(StepCategoryModel::class)
            ->join('`task` ON `task`.step_category_id = `step_category`.id')
            ->where($taskFilter)
            ->field($taskQueryFields)
            ->select();
        $episodeCodeList = $stepCategoryIds = $userIds = $insideSupplierUserId = $outsideSupplierTenantId = $departmentIdMap = [];
        foreach ($tasks as $task) {
            $episodeCodeList[$task['e_initial_episode_code']] = $task['e_initial_episode_code'];
            $stepCategoryIds[$task['parent_step_category_id']] = $task['parent_step_category_id'];
            $userIds[$task['executor']] = $task['executor'];
            $userIds[$task['assignee']] = $task['assignee'];
            $insideSupplierUserId[$task['assignee']] = $task['assignee'];
            if ($task['executor_tenant_id'] > 0 && $task['executor_tenant_id'] != $projectTenantId) {
                $outsideSupplierTenantId[$task['executor_tenant_id']] = $task['executor_tenant_id'];
            }
            if ($task['department_id'] > 0) {
                $departmentIdMap[$task['department_id']] = $task['department_id'];
            }
        }
        unset($episodeCodeList[''], $stepCategoryIds[0], $stepCategoryIds[''], $userIds[0], $userIds[''], $departmentIdMap[0], $outsideSupplierTenantId[0], $insideSupplierUserId[0]);
        $episodeMap = [];
        if (!empty($episodeCodeList)) {
            $episodeMap = EpisodeService::getInstance()->selectEpisode(['project_id' => $projectId, 'code' => ['IN', array_keys($episodeCodeList)]]);
        }
        // 供应商信息
        $supplierRes = $this->queryTaskSupplierInfo($projectTenantId, $insideSupplierUserId, $outsideSupplierTenantId, $departmentIdMap);
        // 查询序列名称
        $shotModuleId = module_by_code('shot')['id'];
        $sequenceIdList = array_column(array_filter($entityList, function ($item) use ($shotModuleId) {
            // 筛选镜头
            return $item['module_id'] == $shotModuleId;
        }), 'entity_id');
        $sequenceList = [];
        if (!empty($sequenceIdList)) {
            $sequenceList = model(EntityModel::class)->where(['id' => ['IN', implode(',', $sequenceIdList)]])->select();
            $sequenceList = array_column($sequenceList, null, 'id');
        }
        foreach ($tasks as &$task) {
            $task['executor_name'] = $task['assignee_name'] = "";
            $task['department_info'] = $supplierRes['department'][$task['department_id']] ?? null;
            if ($task['executor_tenant_id'] != 0 && $task['executor_tenant_id'] != $projectTenantId) {
                $task['supplier_info'] = $supplierRes['outside'][$task['executor_tenant_id']] ?? null;
                $task['executor_name'] = $task['supplier_info']['name'] ?? '';
            } else if ($task['assignee'] > 0) {
                $task['assignee_user'] = $supplierRes['inside'][$task['assignee']] ?? null;
                $task['executor_name'] = $task['assignee_user']['name'] ?? 0;
            }
            if ($task['assignee'] > 0) {
                $task['assignee_name'] = $supplierRes['inside'][$task['assignee']]['name'] ?? null;
            }
        }
        $tasks = array_group_by($tasks, 'entity_id');
        $storyboardModuleId = module_by_code('storyboard')['id'];
        $statusService = StatusService::getInstance();
        $doneStatus = $statusService->getTaskDoneStatus();
        foreach ($entityList as &$entity) {
            $entityId = $entity['eid'];
            $module = Module::$moduleDictData['module_index_by_id'][$entity['module_id']] ?? null;
            $plan = $stepCategoryPlansMap[$module['code'] ?? ""] ?? null;
            if (isset($module['code']) && $module['code'] != "episode") {
                $entity['episode_name'] = $episodeMap[$entity['initial_episode_code']]['name'] ?? '';
            } else {
                $entity['episode_name'] = $entity['name'];
            }
            $entity = [
                'id' => $entity['eid'],
                'name' => $entity['name'],
                'code' => $entity['code'],
                'start_time' => $entity['start_time'],
                'end_time' => $entity['end_time'],
                'duration' => empty($entity['e_duration']) ? 10 : $entity['e_duration'],
                'module_code' => $module['code'] ?? '',
                'entity_id' => $entityId,
                'episode_name' => $entity['episode_name'],
                'executor_name' => '',
                'assignee_name' => '',
                'is_assignee' => $entity['e_is_assignee'],
                'executor_tenant_id' => $projectTenantId,
                'row_type' => "entity",
                'module_id' => $entity['module_id'],
                'parent_entity_id' => $entity['entity_id'],
                'storyboard_sequence' => $entity['storyboard_sequence'],
                'e_json' => $entity['e_json'],
            ];
            // 解析json字段
            $entity = CommonService::appendJsonFields($entity, 'e_json');
            // 追加序列名称
            if ($entity['module_id'] == $shotModuleId) {
                // 镜头
                $entity['sequence_name'] = $sequenceList[$entity['parent_entity_id']]['name'] ?? '';
            } else if ($entity['module_id'] == $storyboardModuleId) {
                // 分镜
                $entity['sequence_name'] = $entity['storyboard_sequence'];
            }
            $childrenTask = $tasks[$entityId] ?? [];
            $entity['plan_start_time'] = $plan['start_time'] ?? 0;
            $entity['plan_end_time'] = $plan['end_time'] ?? 0;
            $donePercent = 0;
            if (!empty($childrenTask)) {
                $childrenTask = array_sort_by($childrenTask, 'step_category_index');
                $doneCount = 0;
                $totalCount = count($childrenTask);
                foreach ($childrenTask as $stepTask) {
                    if (!empty($stepTask['executor_name'])) {
                        $entity['executor_name'] .= $stepTask['executor_name'] . "({$stepTask['name']}),";
                    }
                    if ($stepTask['task_status_id'] == $doneStatus['id']) {
                        $doneCount += 1;
                    }
                }
                $entity['executor_name'] = rtrim($entity['executor_name'], ',');
                unset($childrenTask);
                $donePercent = ($doneCount / $totalCount) * 100;
            }
            $entity['percentDone'] = $donePercent;
            StepService::formatGanttItem($entity, 'startnoearlierthan');
        }
        $result = array_values($entityList);
        return StepService::formatStepCategoryPlanData($result, $project, [], []);
    }

    /**
     * 获得任务默认状态筛选
     * @return array
     */
    public function getNotStartStatusFilter()
    {
        $statusService = StatusService::getInstance();
        $notStartedStatusList = $statusService->getNotStartedStatus();
        return array_column($notStartedStatusList, 'id');
    }

    /**
     * 查询待分配的工序任务
     * @param $param
     * @param int $projectId
     * @param int $episodeId
     * @param $taskIds
     * @param $entityIds
     * @return array
     */
    public function getStepCategoryTaskWaitForAssign($param, int $projectId, int $episodeId, $taskIds, $entityIds)
    {
        $moduleId = module_by_code($param['module_code'])['id'];
        // 只要任务没有开始 那么都可以重新分配
        // 只有组长重置执行人为0后 才能继续分配
        $query = $this->taskModel
            ->where([
                'task.step_type' => 'step',
                'task.project_id' => $projectId,
                'entity.module_id' => $moduleId,
                'task.plan_start_time' => ['gt', 0],
                'task.plan_end_time' => ['gt', 0],
                'task.executor' => 0,
//                "task.executor_tenant_id" => ["IN", implode(',', [0, $tenantId])],
            ])
            ->join("__ENTITY__ AS entity ON task.entity_id = entity.id", "LEFT")
            ->join("__STEP_CATEGORY__ AS sc ON task.step_category_id = sc.id", "LEFT")
            ->order("task.id ASC");
        if (count($taskIds)) {
            $query->where(['task.id' => ['IN', join(',', $taskIds)]]);
        }
        if (count($entityIds)) {
            $query->where(['entity.id' => ['IN', join(',', $entityIds)]]);
        }

        // 默认状态筛选
        $notStartStatusFilterValue = $this->getNotStartStatusFilter();

        if (empty($notStartStatusFilterValue)) {
            $query->where(["JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status'))" => null]);
        } else {
            $notStartStatusFilterValue[] = null;
            $query->where(["JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status'))" => ['IN', implode(',', $notStartStatusFilterValue)]]);
        }
        if ($episodeId) {
            $episode = model(EntityModel::class)->field('code')->find($episodeId);
            if (!empty($episode)) {
                $query->where(['entity.initial_episode_code' => $episode['code']]);
            }
        }

        // 过滤外包任务
        // 过滤子任务已经分配得

        $fields = "
                task.id as task_id, 
                task.name as task_name, 
                task.step_category_id, 
                task.executor,
                task.assignee,
                task.man_hour,
                task.plan_end_time,
                task.plan_start_time,
                task.step_id,
                task.assignee_producer,
                sc.code as step_category_code,
                sc.name as step_category_name, 
                entity.id as entity_id, 
                entity.name as entity_name, 
                entity.code as entity_code, 
                entity.step_workflow_id, 
                entity.json,
                entity.initial_episode_code
                ";

        $tasks = $query
            ->field($fields)
            ->select();

        //关联的集
        $episodesIds = array_column($tasks, "initial_episode_code", 'initial_episode_code');
        $episodes = [];
        if (!empty($episodesIds)) {
            $entityModel = model(EntityModel::class);
            $episodes = $entityModel->where(['code' => ['IN', implode(",", $episodesIds)]])->field("id,name,code")->select();
            $episodes = array_column($episodes, null, 'code');
        }
        $userIdList = [];
        foreach ($tasks as $task) {
            $userIdList[] = $task['executor'];
            $userIdList[] = $task['assignee'];
        }
        $userList = [];
        if (count($userIdList) > 0) {
            $userIdList = array_unique($userIdList);
            $userList = model(UserModel::class)->where(['id' => ['IN', $userIdList]])->field('id,name,avatar')->select();
            $userList = array_column($userList, null, 'id');
        }

        foreach ($tasks as &$task) {
            //当前工序流的工序列表 通过task step_category_id 筛选
            $task['json'] = json_decode($task['json'], true);
            $task['episode'] = $episodes[$task['initial_episode_code']] ?? null;
            $task['executor_user'] = $userList[$task['executor']] ?? null;
            $task['assignee_user'] = $userList[$task['assignee']] ?? null;
        }

        return array_values($tasks);
    }

    /**
     * 查询待分配的工序任务
     * @param array $param
     * @param int $user_id 当前用户id
     * @return mixed
     * @throws Exception
     */
    public function getStepTaskWaitForAssign($param, $user_id): array
    {
        $projectId = $param['project_id'] ?? 0;
        $taskIds = $param['tasks'] ?? "";

        $fields = [
            "task.id",
            "task.name",
            "task.step_category_id",
            "task.assignee",
            "task.project_id",
            "task.entity_module_id",
            "task.entity_id",
            "task.executor",
            "task.assignee",
            "step.id",
            "step.name",
            "step.code",
            "step_category.id",
            "step_category.index",
            "entity.id",
            "entity.name",
            "entity.code",
            "entity.module_id",
            "entity.asset_grade",
            "task.task_status",
            "project.id",
            "project.name",
        ];
        $defaultStatusFilterValue = $this->getNotStartStatusFilter();

        $paramQuery = [
            "fields" => implode(',', $fields),
            "filter" => [
                'task.step_type' => [
                    "eq", "step",
                ],
                'task.assignee' => ['eq', $user_id],
                "_logic" => "AND",
            ],
            "order" => "step_category.index ASC, task.id ASC",
        ];


        if (!empty($defaultStatusFilterValue)) {
            $paramQuery['filter']["task_status.id"] = ["in", implode(',', $defaultStatusFilterValue)];
        } else {
            $paramQuery['filter']["task_status.id"] = null;
        }


        if ($projectId > 0) {
            $paramQuery['filter']["task.project_id"] = ['eq', $projectId];
        }
        if (!empty($taskIds)) {
            $paramQuery['filter']["task.id"] = ['in', $taskIds];

        }


        $service = CommonService::instance("task");
        $data = $service->select($paramQuery, 'task');
        $filterUserIds = array_column(array_column($data['rows'], 'task'), 'executor', 'executor');
        // 查询用户信息
        $userDict = [];
        if (!empty($filterUserIds)) {
            $userModel = model(UserModel::class);
            $userData = $userModel->field('id,name,phone,union_id')->where(['id' => ['IN', join(',', $filterUserIds)]])->select();
            $userDict = array_column($userData, null, 'id');
        }

        $result = [];
        foreach ($data['rows'] as $item) {
            $result[] = [
                "task_id" => $item['task']['id'],
                "task_name" => $item['task']['name'],
                "executorUser" => $userDict[$item['task']['executor']] ?? 0,
                "executor" => $item['task']['executor'],
                "step_category_id" => $item['task']['step_category_id'],
                "step_name" => $item['step']['name'],
                "step_code" => $item['step']['code'],
                "step_id" => $item['step']['id'],
                "module_id" => $item['entity']['module_id'],
                "module_code" => Module::$moduleDictData['module_index_by_id'][$item['entity']['module_id']]['code'],
                "entity_name" => $item['entity']['name'],
                "entity_id" => $item['entity']['id'],
                "assignee" => $item['task']['assignee'],
                "asset_grade" => $item['entity']['asset_grade'],
                'project_id' => $item['project']['id'],
                "project_name" => $item['project']['name'],
                "status" => $item['task_status'] ?? null,
            ];
        }

        return $result;
    }

    /**
     * 获取个人执行任务统计数据
     * @param $userId
     * @return array
     */
    public function getPersonalTaskStatistics($userId)
    {
        // 获取状态id分组通过状态从属关系
        $statusService = StatusService::getInstance();
        $allStatusData = $statusService->getStatusIdGroupByCorrespond();

        // 历史项目不统计, 项目状态为 done 或者 hide
        $projectModel = model(ProjectModel::class);
        $projectExcludeStatusIds = join(',', array_merge($allStatusData['done'], $allStatusData['hide']));
        $excludeProjects = $projectModel->field('id')->where(['_string' => "json_extract(json,'$.project_status') IN ('{$projectExcludeStatusIds}')"])->select();
        $excludeProjectIds = array_column($excludeProjects, 'id');

        /**
         * 警告任务：
         * 反馈任务：状态类型为 feedback 的任务
         * 今日任务：截止时间是今天的任务
         * 本周任务：任务时间范围在这周内的
         * 审核任务：状态类型为 daily 的任务
         * 完成任务：状态类型为 done、hide 的任务
         */

        $taskStatistics = [
            'warning' => 0,
            'feedback' => 0,
            'today' => 0,
            'this_week' => 0,
            'daily' => 0,
            'done' => 0,
        ];

        // 反馈任务数量
        $taskModel = model(TaskModel::class);

        if (!empty($allStatusData['feedback'])) {
            $taskFeedBackStatusIds = join(',', $allStatusData['feedback']);

            $taskStatistics['feedback'] = $taskModel->where([
                'executor' => $userId,
                'project_id' => ['NOT IN', join(',', $excludeProjectIds)],
                '_string' => "json_extract(json,'$.task_status') IN ('{$taskFeedBackStatusIds}')",
            ])->count();
        }

        // 审核任务数量
        if (!empty($allStatusData['daily'])) {
            $taskDailyBackStatusIds = join(',', $allStatusData['daily']);

            $taskStatistics['daily'] = $taskModel->where([
                'executor' => $userId,
                'project_id' => ['NOT IN', join(',', $excludeProjectIds)],
                '_string' => "json_extract(json,'$.task_status') IN ('{$taskDailyBackStatusIds}')",
            ])->count();
        }

        // 完成任务数量
        if (!empty($allStatusData['done']) || !empty($allStatusData['hide'])) {
            $taskDoneBackStatusIds = join(',', array_merge($allStatusData['done'], $allStatusData['hide']));

            $taskStatistics['done'] = $taskModel->where([
                'executor' => $userId,
                'project_id' => ['NOT IN', join(',', $excludeProjectIds)],
                '_string' => "json_extract(json,'$.task_status') IN ('{$taskDoneBackStatusIds}')",
            ])->count();
        }

        // 今天任务
        $todayStartTime = strtotime(date('Y-m-d 00:00:00'));
        $todayEndTime = strtotime(date('Y-m-d 23:59:59'));
        $taskStatistics['today'] = $taskModel->where([
            'executor' => $userId,
            'project_id' => ['NOT IN', join(',', $excludeProjectIds)],
            'plan_end_time' => ['BETWEEN', [$todayStartTime, $todayEndTime]],
        ])->count();


        // 这周任务
        $weekTimeRange = get_current_week_range();
        $taskStatistics['this_week'] = $taskModel->where([
            'executor' => $userId,
            'project_id' => ['NOT IN', join(',', $excludeProjectIds)],
            '_complex' => [
                'plan_start_time' => ['BETWEEN', [$weekTimeRange['sdate'], $weekTimeRange['edate']]],
                'plan_end_time' => ['BETWEEN', [$weekTimeRange['sdate'], $weekTimeRange['edate']]],
                '_logic' => 'OR',
            ],
        ])->count();

        return $taskStatistics;
    }


    /**
     * 设置工序任务执行人
     * 支持重置任务的执行人 @update 2020年11月14日
     * @param int $taskId 任务id
     * @param int $assignUserId 分配人 user_id
     * @param int $executorTenantId 执行人租户id
     * @param string $supplierType 分类类型
     * @return array|bool
     * @throws Exception
     * @throws Throwable
     */
    public function setStepTaskExecutor($taskId, $assignUserId, int $executorUserId, int $executorTenantId, $supplierType = 'inside')
    {
        $executorUser = null;
        if ($executorUserId > 0) {
            $executorUser = model(UserModel::class)->find($executorUserId);
        }

        $taskModel = model(TaskModel::class)->forceMasterDB();
        // 只有任务为默认状态的 才能继续分配给别人
//        $statusService = StatusService::getInstance();
//        $notStartStatusList = $statusService->getNotStartedStatus();
//        $defaultStatusFilterValue = array_column($notStartStatusList, 'id');

        //只有未开始的任务才可以分配给别人
//        $statusModel = model(StatusModel::class);
//        $statusIds = $statusModel->where(['correspond' => "not_started"])->field("id")->select();
//        $statusIds = implode(',', array_column($statusIds, 'id'));

//        if (!empty($defaultStatusFilterValue)) {
//            $defaultStatusConditionValue = " IN  (" . implode(',', $defaultStatusFilterValue) . ")";
//        } else {
//            $defaultStatusConditionValue = "IS NULL";
//        }
        //检查任务是否是未开始状态
//        $task = $taskModel->where("JSON_CONTAINS('[$statusIds]'  ,JSON_UNQUOTE(JSON_EXTRACT(json,'$.task_status'))) AND id = {$taskId}")->field("id,step_type")->find();
        $task = $taskModel->find($taskId);
//        $task = $taskModel->where("JSON_UNQUOTE(JSON_EXTRACT(json,'$.task_status')) {$defaultStatusConditionValue} AND id = {$taskId}")->find();
        if (empty($task)) {
//            throw new LogicException("missing task [{$taskId}] or status is not [{$defaultStatusConditionValue}]", ErrorCode::STATUS_IS_NOT_DEFAULT_STATUS_VALUE);
            throw new LogicException("missing task [{$taskId}]", ErrorCode::TASK_NOT_FOUND);
        }

        //查出来的任务不是工序任务
        if ($task['step_type'] != "step") {
            throw new LogicException("[{$taskId}] is not an step task", ErrorCode::NOT_A_STEP_TASK);
        }

        $planTime = PlanService::getInstance()->getStepCategoryPlanEndTimeOne($task['episode_id'], $task['step_category_id']);
        if ($supplierType === 'outside') {
            // 分配给外部供应商
            $assignUser = model(UserModel::class)->where(['id' => $assignUserId])->field('id,name,union_id')->find();
            return $this->sendStepTaskToOutSideSupplier($task, $assignUser, $executorUser, $executorTenantId, $planTime);
        } else {

            // 分配给内部

            // ready状态检查
            Client::send('task_ready_check', build_queue_data_with_xu(['task_id' => $taskId]));
            if ($executorUserId == 0) {
                // 置空执行人的时候 清空分配标识

                $flag = $taskModel->save([
                    'id' => $taskId,
                    'executor' => $executorUserId,
                    'executor_tenant_id' => 0,
                    'is_assignee' => 'no',
                    'assign_status' => TaskModel::ASSIGN_STATUS_DEFAULT,
                ]);
            } else {

                $changeExecutorData = [
                    'id' => $taskId,
                    'executor' => $executorUserId,
                    'executor_tenant_id' => $executorTenantId,
                    'is_assignee' => 'yes',
                    'assignee' => $assignUserId,
                    'assign_status' => TaskModel::ASSIGN_STATUS_EXECUTOR,
                ];
                if ($task['plan_delivery_time'] <= 0 && !empty($planTime)) {
                    $changeExecutorData['plan_delivery_time'] = $planTime;
                }
                $flag = $taskModel->save($changeExecutorData);
            }
            if ($flag === false) {
                throw new LogicException("set task executor failure" . $taskModel->getError(), ErrorCode::SET_TASK_EXECUTOR_FAILURE);
            }
            return $taskModel->find($taskId);
        }
    }

    /**
     * 组长任务树
     * @param $params
     * @param int $userId
     * @param int $tenantId
     * @return array
     */
    public function getTeamLeaderTaskTree($params, int $userId, int $tenantId)
    {
        $order = $params['param']['order'] ?? 'task.assignee_at DESC,task.id ASC';
        $queryField = [
            "task.id as id",
            "task.name as name",
            "task.code as code",
            "task.type as type",
            "task.plan_start_time as start_time",
            "task.plan_end_time as end_time",
            "task.plan_duration as duration",
            "task.entity_module_id as module_id",
            "task.step_type",
            "task.project_id",
            "task.executor",
            "task.assignee",
            "task.entity_id",
            "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status')) as task_status",
            "task.pre_task_id as pre_task_id",
        ];

        $entitySearchFilter = [
            'tenant_id' => $tenantId,
        ];
        $where = [
            'task.assignee' => $userId,
            "task.step_type" => "step",
            'task.type' => 'sub', //只查询普通任务 工序任务
            'task.tenant_id' => $tenantId,
        ];

        if (!empty($params['param']['project_id'])) {
            $where['task.project_id'] = (int)$params['param']['project_id'];
            $entitySearchFilter['project_id'] = (int)$params['param']['project_id'];
        }

        if (array_key_exists('only_total', $params['param']) && $params['param']['only_total'] === "yes") {
            return success_response('OK', 1);
        }

        // 实体名称搜索
        if (!empty($params['param']['filter']['entity.name'])) {
            $entitySearchFilter['name'] = $params['param']['filter']['entity.name'];
            $entityIdList = model(EntityModel::class)->where($entitySearchFilter)->field('id')->select();
            if (count($entityIdList) > 0) {
                $where['task.entity_id'] = ['IN', array_column($entityIdList, 'id')];
            } else {
                $where['task.entity_id'] = null;
            }
        }

        $tasks = model(TaskModel::class)->where($where)->field($queryField)->order($order)->select();
        $entityIdList = $userIds = $projectIds = $statusIds = [];
        foreach ($tasks as $task) {
            $entityIdList[$task['entity_id']] = $task['entity_id'];
            $userIds[$task['executor']] = $task['executor'];
            $userIds[$task['assignee']] = $task['assignee'];
            $projectIds[$task['project_id']] = $task['project_id'];
            $statusIds[$task['task_status']] = $task['task_status'];
        }
        unset($userIds[0], $entityIdList[0], $statusIds[0], $statusIds['']);
        // 查询实体数据
        $entityData = [];
        if (!empty($entityIdList)) {
            $entityData = model(EntityModel::class)->where(['id' => ['IN', array_values($entityIdList)]])->select();
        }
        // 查询关联信息
        $episodeMap = $parentEntityMap = [];
        if (!empty($entityData)) {
            $filterEpisodes = [];
            $parentEntityIdList = [];
            foreach ($entityData as $entityItem) {
                if (!empty($entityItem['initial_episode_code']) && (!array_key_exists($entityItem['project_id'], $filterEpisodes) || !in_array($entityItem['initial_episode_code'], $filterEpisodes[$entityItem['project_id']]))) {
                    $filterEpisodes[$entityItem['project_id']][] = $entityItem['initial_episode_code'];
                }
                $parentEntityIdList[$entityItem['entity_id']] = $entityItem['entity_id'];
            }
            // 初始集
            if (!empty($filterEpisodes)) {
                $episodeMap = EpisodeService::getInstance()->getMultipleProjectEpisodes($filterEpisodes);
            }
            // 父级实体信息
            $parentEntityData = model(EntityModel::class)->where(['id' => ['IN', implode(',', $parentEntityIdList)]])->select();
            foreach ($parentEntityData as $parentEntity) {
                $parentEntity['json_fields'] = json_decode($parentEntity['json'] ?? '{}', true);
                $parentEntity = array_merge($parentEntity, $parentEntity['json_fields']);
                $parentEntityMap[$parentEntity['id']] = $parentEntity;
            }
            unset($parentEntityData);
        }

        $entityData = array_column($entityData, null, 'id');
        $entityData = array_map(function ($item) use ($parentEntityMap, $episodeMap) {
            $item['json_fields'] = json_decode($item['json'] ?? '{}', true);
            $item = array_merge($item, $item['json_fields']);
            $item['real_id'] = $item['id'];
            $item['id'] = 'entity_' . $item['id'];
            unset($item['json']);
            if (isset($parentEntityMap[$item['entity_id']])) {
                $parentEntity = $parentEntityMap[$item['entity_id']];
                $parentModule = Module::$moduleDictData['module_index_by_id'][$parentEntity['module_id']] ?? ['code' => 'unknown'];
                $item[$parentModule['code']] = $parentEntity;
            }
            $item['episode'] = $episodeMap[$item['project_id']][$item['initial_episode_code']] ?? null;
            return $item;
        }, $entityData);

        $tasks = array_column($tasks, null, 'id');
        // 查询用户信息
        $users = [];
        if (!empty($userIds)) {
            $users = model(UserModel::class)->where(['id' => ['IN', join(',', $userIds)]])->field("id,name,email")->select();
            $users = array_column($users, null, 'id');
        }
        //查询项目信息
        $projectData = [];
        if (!empty($projectIds)) {
            $projectService = ProjectService::getInstance();
            $projectData = $projectService->getProjects(['id' => ['IN', array_values($projectIds)]], "id,name,code,belong_project_id");
            $projectData = array_column($projectData, null, 'id');
        }

        //查询状态信息
        $statusData = [];
        if (!empty($statusIds)) {
            $statusData = model(StatusModel::class)->where(['id' => ['IN', implode(',', $statusIds)]])->field("id,name,color,code,correspond,icon")->select();
            $statusData = array_column($statusData, null, 'id');
        }
        $dependency = [];
        $tasksGroupByEntity = array_group_by($tasks, 'entity_id');
        //填充关联的数据
        $startTime = time();
        $treeData = [];
        foreach ($tasksGroupByEntity as $entityId => &$tasks) {
            $entity = $entityData[$entityId];
            $entity['pid'] = 0;
            if (isset($projectData[$entity['project_id']])) {
                $entity['project_name'] = $projectData[$entity['project_id']]['name'];
            }
            StepService::formatGanttItem($entity, 'startnoearlierthan');
            foreach ($tasks as &$task) {
                if ($startTime >= $task['start_time'] || empty($startTime)) {
                    $startTime = $task['start_time'];
                }
                $task['executor_name'] = $task['assignee_name'] = "";
                $task['executor_name'] = $users[$task['executor']]['name'] ?? '';
                $task['assignee_name'] = $users[$task['assignee']]['name'] ?? '';
                unset($task['assignee'], $task['executor']);
                $task = array_merge($task, json_decode($task['entity_json'] ?? '{}', true) ?? []);
                $task['project_name'] = $projectData[$task['project_id']]['name'] ?? '';
                $task['task_status_name'] = $statusData[$task['task_status']]['name'] ?? '';
                $task['duration'] = empty($task['e_duration']) ? 10 : $task['e_duration'];
                $task['module_code'] = Module::$moduleDictData['module_index_by_id'][$entity['module_id']]['code'];
                $task['entity_name'] = $entity['name'];
                $task['pid'] = $entity['id'];
                if ($task['pre_task_id']) {
                    $dependency[$task['id']] = [
                        'id' => $task['id'],
                        'fromTask' => $task['pre_task_id'],
                        'toTask' => $task['id'],
                    ];
                }
                $task = array_merge($task, $entity['json_fields']);
                StepService::formatGanttItem($task, 'startnoearlierthan');
            }
            unset($entity['json_fields']);
            $entity['children'] = $tasks;
            $treeData[] = $entity;
        }

        return StepService::formatStepCategoryPlanData($treeData, ['start_time' => $startTime], array_values($dependency));
    }

    /**
     * 查询任务的工序流配置
     * @param $param
     * @return array
     */
    public function getTaskStepWorkFlowNetwork($param)
    {
        $taskId = $param['task_id'];
        $entityModel = model(EntityModel::class);
        $taskModel = model(TaskModel::class);
        $stepWorkFlowService = StepWorkflowService::getInstance();

        $entityId = $taskModel->where(['id' => $taskId])->getField("entity_id");
        $workFlowId = $entityModel->where(['id' => $entityId])->getField("step_workflow_id");
        if (empty($workFlowId)) {
            throw new LogicException("can`t found task entity", ErrorCode::TASK_ENTITY_NOT_FOUND);
        }
        $stepWorkFlow = $stepWorkFlowService->getStepWorkFlow($workFlowId, 'id,config');
        if (empty($stepWorkFlow) || empty($stepWorkFlow['config'])) {
            throw new LogicException("can`t found task workflow config", ErrorCode::TASK_WORKFLOW_CONFIG_NOT_EXIST);
        }
        $stepConfig = json_decode($stepWorkFlow['config'], true);
        $stepConfig['edges'] = array_map(function ($item) {
            $item['value'] = "";
            return $item;
        }, $stepConfig['edges']);
        $nodes = [];
        foreach ($stepConfig['nodes'] as $node) {
            $nodes = array_merge($nodes, $node);
        }
        return ['edges' => $stepConfig['edges'], 'stepList' => $nodes];
    }

    /**
     * 获取指定实体的指定二级分类工序任务列表
     * @param $entityId
     * @param int $stepCategoryId
     * @param string $taskId
     * @return array|false|mixed|string
     */
    public function getEntityStepTaskList($entityId, $stepCategoryId = 0, $taskId = '')
    {
        //如果为二级分类工序为空则查询所有工序
        $where = [
            'entity_id' => $entityId,
            'step_type' => 'step',
        ];
        if (!empty($stepCategoryId)) {
            $where['step_category_id'] = $stepCategoryId;
        }

        if (!empty($taskId)) {
            $stepCategoryId = model(TaskModel::class)
                ->where([
                    'id' => $taskId,
                ])
                ->getField('step_category_id');
            $where['step_category_id'] = $stepCategoryId;
        }

        $taskList = model(TaskModel::class)
            ->where($where)
            ->field("id,name,code,step_id,entity_id,step_category_id,json_unquote(json_extract(json,'$.task_status')) as task_status_id,end_time")
            ->select();

        return $taskList ?? [];
    }

    /**
     * 查询任务详情
     * @param $param
     * @return array
     * @throws \Exception
     */
    public function getTaskDetail($param): array
    {
        $taskId = $param['task_id'];
        $projectService = ProjectService::getInstance();

        $task = $this->taskModel->where(['id' => $taskId])->find();
        if (empty($task)) {
            throw new LogicException('task not found', ErrorCode::TASK_NOT_FOUND);
        }
        $task['entity'] = model(EntityModel::class)->field("id,name,module_id,code,json,showings_number,initial_episode_code,step_workflow_id")->find($task['entity_id']);
        if (!empty($task['entity']['module_id'])) {
            request()->getModuleCode(module_by_id($task['entity']['module_id'])['code'] ?? 'entity');
            CommonService::getFormatAttrData(model(EntityModel::class), $task['entity']);
        }
        $task['step'] = model(StepModel::class)->find($task['step_id']);
        $task['project'] = $projectService->getProject($task['project_id'], 'id,name,code,type,work_dir,work_dir_prefix');

        $userList = [];
        $userList[] = $task['executor'];
        $userList[] = $task['assignee'];
        $userList[] = $task['assignee_producer'];
        $userList = array_filter(array_unique($userList), function ($item) {
            return $item > 0;
        });
        if (count($userList) > 0) {
            $userList = model(UserModel::class)->where(['id' => ['IN', implode(',', $userList)]])->field('id,name,avatar')->select();
            $userList = array_column($userList, null, 'id');
        }
        $task['executorUser'] = $userList[$task['executor']] ?? null;
        $task['assignee_user'] = $userList[$task['assignee']] ?? null;
        $task['assignee_producer'] = $userList[$task['assignee_producer']] ?? null;

        $task['json'] = json_decode($task['json'], true);
        $task['task_tag'] = $task['json']['task_tag'] ?? null;
        $task['tags'] = [];
        if (!empty($task['task_tag'])) {
            $tags = model(TagModel::class)->where(['id' => ['IN', $task['task_tag']]])->select();
            $tags = array_map(function ($item) {
                $item['type'] = "tag";
                return $item;
            }, $tags);

            $task['tags'] = $tags;
            $task['task_tag'] = $tags;
        }

        $task['task_status'] = $task['json']['task_status'] ?? null;

        if (!empty($task['task_status'])) {
            $status = model(StatusModel::class)->where(['id' => $task['task_status']])->find();
            $status['type'] = "status";
            $task['tags'][] = $status;
            $task['task_status'] = $status;
        }

        $task['task_list'] = model(TaskModel::class)
            ->where([
                'entity_id' => $task['entity_id'],
                'step_category_id' => $task['step_category_id'],
                'step_type' => 'step',
            ])
            ->field("id,name,code,step_id,step_category_id,json_unquote(json_extract(json,'$.task_status')) as task_status_id")
            ->select();
        $statusIdArray = array_column($task['task_list'], 'task_status_id', 'task_status_id');
        $statusMap = [];
        if (!empty($statusIdArray)) {
            $statusMap = model(StatusModel::class)->where(['id' => ['IN', implode(',', $statusIdArray)]])->select();
            $statusMap = array_column($statusMap, null, 'id');
        }
        $task['task_list'] = array_map(function ($item) use ($statusMap) {
            $item['task_status'] = $statusMap[$item['task_status_id']] ?? null;
            return $item;
        }, $task['task_list']);

        $task['latest_version'] = $this->getLatestVersion($taskId);
        $task['step_category'] = model(StepCategoryModel::class)->where(['id' => $task['step_category_id']])->find();

        //task attr
        $taskAttr = '';
        try {
            $taskAttr = TaskService::getInstance()->getTaskStandardDocAttrStrById($taskId);
        } catch (Throwable $e) {
            trace(format_exception_info($e), 'ERR');
        }
        $task['task_attr'] = $taskAttr;

        // 查询当前时间日志
        $timelogService = TimelogService::getInstance();
        $timelogTaskData = $timelogService->getTaskActiveTimer($taskId);
        $task['is_active'] = !empty($timelogTaskData);
        $task['timelog'] = $timelogTaskData;
        // 查询历史执行人信息
        $task['executor_records'] = $this->getTaskExecutorRecords($taskId);
        $this->execDismantleTaskInfo($task);
        $task['dismantle_task_finished'] = $this->getDismantleTaskFinished($taskId); // 获取主任务下面的子任务是否否完成了
        return $task;
    }

    /**
     * 查询主任务下的子任务是否都完成了
     * @param $taskId
     * @return bool|string
     */
    public function getDismantleTaskFinished($taskId)
    {
        if (empty($taskId)) {
            return '';
        }
        $finished = false;
        $finalData = model(StatusModel::class)->where(['code' => 'final'])->find();
        $taskProjectData = model(TaskModel::class)->where(['id' => $taskId])->field('project_id')->find();
        $taskProjectId = $taskProjectData['project_id'] ?? 0;
        $taskRelationData = model(TaskRelationModel::class)
            ->setSuffix(sprintf('_project_%d', $taskProjectId))
            ->alias('task_relation')
            ->join("task on task_relation.target_task_id = task.id")
            ->field("source_task_id, task.id, task.json")
            ->where(['relation_type' => 'dismantle_task', 'task_relation.source_task_id' => $taskId])
            ->select();
        if (empty($taskRelationData)) {
            return '';
        }
        $finNum = 0;
        foreach ($taskRelationData as $value) {
            $jsonArr = json_decode($value['json'], true);
            if ($jsonArr['task_status'] == $finalData['id']) {
                $finNum++;
            }
        }
        if (count($taskRelationData) == $finNum) {
            $finished = true;
        }
        return $finished;
    }

    /**
     * 转换子任务返回参数
     * @return void
     */
    public function execDismantleTaskInfo(&$task)
    {
        $taskJson = $task['json'];
        if (!empty($taskJson['is_dismantle_task']) && $taskJson['is_dismantle_task'] == 'true') {
            $task['entity']['name'] = $taskJson['dismantle_name'] ?? '';
            $task['entity']['code'] = $task['code'] ?? '';
        }
    }

    /**
     * 查询任务历史执行人记录
     * @param int $taskId
     * @return array|false|mixed|string
     */
    public function getTaskExecutorRecords(int $taskId)
    {
        $list = model(TaskExecutorRecordModel::class)->where(['task_id' => $taskId])->select();
        $userIdList = [];
        foreach ($list as $item) {
            $userIdList[$item['user_id']] = $item['user_id'];
            $userIdList[$item['next_user_id']] = $item['next_user_id'];
            $userIdList[$item['created_by']] = $item['created_by'];
        }
        $userList = [];
        if (!empty($userIdList)) {
            $userList = model(UserModel::class)->where(['id' => ['IN', implode(',', $userIdList)]])->field('id,name,avatar')->select();
            $userList = array_column($userList, null, 'id');
        }
        foreach ($list as &$item) {
            $item['user'] = $userList[$item['user_id']] ?? null;
            $item['next_user'] = $userList[$item['next_user_id']] ?? null;
            $item['created_user'] = $userList[$item['created_by']] ?? null;
        }
        return $list;
    }

    /**
     * 查询分类=>任务 树形数据
     * @param $filter
     * @param bool $onlyTotal
     * @param array $page
     * @return array
     */
    public function getTaskTreeGroupByStepCategory($filter, bool $onlyTotal, array $page)
    {
        /**
         * 通过分类code查询二级分类
         * 查询项目数据 作为甘特时间限制
         * 查询任务数据
         * 组装成 分类=>任务的树格式
         */
        $stepCategoryParent = model(StepCategoryModel::class)
            ->where(['code' => ['IN', implode(',', $filter['step_category_code'])]])
            ->field("id,name,code,entity_id")
            ->select();
        if (empty($stepCategoryParent)) {
            throw_strack_exception('unknown step_category_code', ErrorCode::UNKNOWN_STEP_CATEGORY_CODE);
        }
        $stepCategoryParent = array_column($stepCategoryParent, null, 'id');
        $entityModuleIdList = array_column($stepCategoryParent, 'entity_id');
        $categories = model(StepCategoryModel::class)
            ->where(['parent_id' => ['IN', array_column($stepCategoryParent, 'id')]])
            ->field("id,name,code,parent_id")
            ->select();
        $categories = array_column($categories, null, 'id');
        $categoryIds = array_keys($categories);
        $project = model(ProjectModel::class)->field('id,name,code,start_time,end_time,delivery_time')->find($filter['project_id']);
        if (empty($project)) {
            throw_strack_exception('missing project', ErrorCode::MISSING_PROJECT_ID_PARAM);
        }
        $queryField = [
            "task.id as task_id",
            "task.name as task_name",
            "task.code as task_code",
            "task.type as task_type",
            "task.plan_start_time as start_time",
            "task.plan_end_time as end_time",
            "task.plan_duration as duration",
            "task.executor as executor",
            "task.assignee as assignee",
            "task.step_category_id",
            "entity.id as entity_id",
            "entity.name as entity_name",
            "entity.code as entity_code",
            "entity.initial_episode_code",
            "entity.module_id as module_id",
            "entity.json as e_json",
        ];
        $where = [
            'task.step_category_id' => ['IN', implode(',', $categoryIds)],
            'entity.project_id' => $project['id'],
            'entity.module_id' => ['IN', $entityModuleIdList],
        ];
        //任务类型筛选
        if (isset($filter['task_step_type'])) {
            $where['task.step_type'] = $filter['task_step_type'];
        }
        //父级实体id筛选
        if (isset($filter['entity_id'])) {
            $where['entity.entity_id'] = $filter['entity_id'];
        }
        //分页 总行数
        if ($onlyTotal) {
            return model(EntityModel::class)->join('task on entity.id = task.entity_id')->where($where)->count();
        }

        $tasks = model(EntityModel::class)->join('task on entity.id = task.entity_id')->where($where)->field($queryField)->page($page[0], $page[1])->select();
        // 遍历出
        $userIds = [];
        foreach ($tasks as $task) {
            $userIds[$task['executor']] = $task['executor'];
            $userIds[$task['assignee']] = $task['assignee'];
        }
        unset($userIds[0]);
        $tasks = array_column($tasks, null, 'tid');

        // 查询用户信息
        $users = [];
        if (!empty($userIds)) {
            $users = model(UserModel::class)->where(['id' => ['IN', join(',', $userIds)]])->field("id,name,email")->select();
            $users = array_column($users, null, 'id');
        }

        foreach ($tasks as &$task) {
            $task = CommonService::appendJsonFields($task, 'e_json');
            $task['executor_name'] = $users[$task['executor']]['name'] ?? '';
            $task['assignee_name'] = $users[$task['assignee']]['name'] ?? '';
            unset($task['executor'], $task['assignee']);
        }
        //通过分类id分组
        $tasksGroupByStepCategory = array_group_by($tasks, 'step_category_id');

        $result = [];
        foreach ($tasksGroupByStepCategory as $stepCategoryId => $tasksGrouped) {
            foreach ($tasksGrouped as &$taskItem) {
                $taskItem['id'] = $taskItem['task_id'];
                $taskItem['name'] = $taskItem['entity_name'];
                $taskItem['code'] = $taskItem['task_code'];
                StepService::formatGanttItem($taskItem);
            }
            //第一层是step_category
            if (empty($stepCategoryId) || !isset($categories[$stepCategoryId])) {
                //没有关联的  直接merge到和分类同级
                $result = array_merge($result, $tasksGrouped);
                continue;
            }
            $tmp = $categories[$stepCategoryId];
            $tmp['category_code'] = $stepCategoryParent[$tmp['parent_id']]['code'];
            $tmp['children'] = $tasksGrouped;
            $tmp['entity_id'] = 0;
            $tmp['id'] = "s" . $tmp['id'];
            StepService::formatGanttItem($tmp);
            $result[] = $tmp;
        }
        return StepService::formatStepCategoryPlanData($result, $project);
    }

    /**
     * 更新任务的开始时间和结束时间
     * 需要判断计划的时间
     * @param $data
     * @return array
     * @throws Throwable
     */
    public function updateSEDateBatch($data)
    {
        $taskModel = model(TaskModel::class);
        $res = [];
        $taskModel->startTrans();
        try {
            foreach ($data as $taskData) {
                $res[] = $this->updateSEDate($taskData);
            }
            $taskModel->commit();
            return $res;
        } catch (Throwable $e) {
            $taskModel->rollback();
            throw $e;
        }
    }

    /**
     * 更新任务的开始时间和结束时间
     * @param $taskData
     * @return array|bool
     * @throws \Exception
     */
    public function updateSEDate($taskData)
    {
        $taskModel = model(TaskModel::class);
        $planModel = model(PlanModel::class);

        $task = $taskModel->field("id,project_id,step_category_id,step_type")->find($taskData['id']);
        if (empty($task)) {
            throw new LogicException("task not exist", ErrorCode::TASK_NOT_EXIST);
        }

        $checkTarget = [];
        if ($task['step_type'] == "step") {
            //子任务 去查父级任务
            $parentTask = $taskModel
                ->alias("t")->join("__TASK_RELATION__ AS tr ON  t.id = tr.source_task_id")
                ->where(['tr.target_task_id' => $task['id']])
                ->field("t.id,t.plan_start_time,t.plan_end_time")
                ->find();
            if (!empty($parentTask)) {
                $checkTarget['start_time'] = $parentTask['plan_start_time'];
                $checkTarget['end_time'] = $parentTask['plan_end_time'];
            }

        } elseif ($task['step_type'] == "step_category") {
            //分类任务 去查任务计划
            //todo 外包的影子任务 需要查项目 然后查原始项目计划  现阶段影子项目不会修改分类任务的计划日期

            $parentCategory = model(StepCategoryModel::class)->where(['id' => $task['step_category_id']])->field("parent_id")->find();

            if (!empty($parentCategory)) {
                $plan = $planModel->where(
                    [
                        'project_id' => $task['project_id'],
                        'tenant_id' => auto_fill_tenant_id(),
                        'step_category_id' => $parentCategory['parent_id'],
                    ]
                )
                    ->field("start_time,end_time")
                    ->find();
                if (!empty($plan)) {
                    $checkTarget['start_time'] = $plan['start_time'];
                    $checkTarget['end_time'] = $plan['end_time'];
                }
            }
        }

        // 如果有检查的数据  那么就检查计划的开始时间和结束时间
        if (!empty($checkTarget)) {
            // 任务的开始时间和结束时间 必须在项目的工序分类的计划区间内
            if ($checkTarget['start_time'] > strtotime($taskData['plan_start_time'])
                || $checkTarget['end_time'] < strtotime($taskData['plan_end_time'])) {
                throw new LogicException("task plan time should between project/(parent task) Plan time", ErrorCode::PLAN_TIME_SHOULD_BETWEEN_PROJECT_OR_PARENT_TASK);
            }
        }
        $res = $taskModel->modifyItem($taskData);
        if ($res === false) {
            if ($taskModel->getErrorCode() == -411112) {
                return $res;
            }
            throw new LogicException($taskModel->getError(), ErrorCode::TASK_MODIFY_FAILURE);
        }
        return $res;
    }


    /**
     * 工时(8小时制) 转换为正常的24小时制 返回天和剩下的小时
     * @param float $hours
     * @param int $unit
     * @return array
     */
    public static function separateManHour($hours, int $unit = 8)
    {
        // 避免不是数字
        if (!is_numeric($hours)) {
            return [0, 0];
        }
        $days = floor($hours / $unit);
        $leftHours = $hours - $days * $unit;
        return [$days, $leftHours];
    }

    /**
     * 获得任务的检查点
     * @param $params
     * @return array
     * @throws \Exception
     */
    public function getCheckListWithProgress($params)
    {
        $taskId = $params['filter']['task_id'];
        $taskModel = model(TaskModel::class);
        $versionCheckListModel = model(VersionCheckListModel::class);
        $taskModuleData = Module::$moduleDictData['module_index_by_code']['task'] ?? null;

        if (empty($taskModuleData)) {
            throw_strack_exception("missing module data", ErrorCode::MISSING_MODULE_PARAM);
        }

        $task = $taskModel
            ->join("entity on task.entity_id = entity.id", "LEFT")
            ->where(['task.id' => $taskId])
            ->field("task.step_id,task.man_hour,task.plan_start_time,entity.step_workflow_id")
            ->find();

        if (empty($task)) {
            throw_strack_exception("task not exist", ErrorCode::TASK_NOT_EXIST);
        }
        $checkList = $this->getTaskTotalCheckList($taskId);
        $checkList = array_column($checkList, null, 'id');
        $checkList = array_sort_by($checkList, "percentage", "asc");
        $checkListIds = array_column($checkList, 'id');
        $stepIdList = array_column($checkList, 'step_id');

        $reviewWorkflowService = ReviewWorkflowService::getInstance();

        $stepReviewWorkflows = $reviewWorkflowService->selectStepReviewWorkFlowByStepReviewRelation(['step_id' => ['IN', implode(',', $stepIdList)]], 'rw.*,srw.step_id');
        $stepReviewWorkflows = array_column($stepReviewWorkflows, null, 'step_id');
        $roleCodeList = [];
        $stepReviewWorkflows = array_map(function ($item) use (&$roleCodeList) {
            $item['config'] = json_decode($item['config'], true);
            $item['review_role_code'] = array_column($item['config']['nodes']['reviewer'] ?? [], 'role');
            $roleCodeList = array_merge($roleCodeList, $item['review_role_code']);
            return $item;
        }, $stepReviewWorkflows);
        $roleCodeList = array_unique($roleCodeList);
        $roleDatas = teamones_request('teamones-im', 'role/select', ['param' => [
            'filter' => [
                'role.code' => [
                    '-in', implode(',', $roleCodeList),
                ],
            ],
            'fields' => 'role.id,role.code,role.name',
        ]])['data'];
        $roleDatas = array_column($roleDatas['rows'], null, 'code');
        foreach ($stepReviewWorkflows as &$reviewWorkflow) {
            $reviewWorkflow['review_roles'] = array_map(function ($item) use ($roleDatas) {
                return ['role' => $roleDatas[$item]];
            }, $reviewWorkflow['review_role_code']);
        }

        //已通过的版本 checkList
        $versionCheckList = $versionCheckListModel
            ->alias("vcl")
            ->join("__VERSION__ AS v ON vcl.version_id = v.id", "LEFT")
            ->where(
                [
                    'vcl.check_list_id' => ["IN", implode(',', $checkListIds)],
                    'v.link_id' => $taskId,
                    'v.module_id' => $taskModuleData['id'],
                    'v.is_pass' => "yes",
                ])
            ->field("vcl.id,vcl.check_list_id,vcl.version_id")
            ->select();
        $versionCheckList = array_column($versionCheckList, null, 'check_list_id');


        $versionModule = Module::$moduleDictData['module_index_by_code']['version'];
        //审核中的版本 checkList
        $versionReviewingCheckList = $versionCheckListModel
            ->alias("vcl")
            ->join("__VERSION__ AS v ON vcl.version_id = v.id", "LEFT")
            ->join("review_request on v.id = review_request.link_id AND review_request.module_id = {$versionModule['id']}")
            ->where(
                [
                    'vcl.check_list_id' => ["IN", implode(',', $checkListIds)],
                    'v.link_id' => $taskId,
                    'v.module_id' => $taskModuleData['id'],
                    'review_request.is_reviewed' => "no",
                ])
            ->field("vcl.id,vcl.check_list_id,vcl.version_id")
            ->select();
        $versionReviewingCheckList = array_column($versionReviewingCheckList, null, 'check_list_id');

        $result = [];
        $latestKey = 0;
        $apsService = new ApsService();
        foreach ($checkList as $key => $checkItem) {
            $tmp = [
                'id' => $checkItem['id'],
                'name' => $checkItem['content'],
                'plan_time' => 0,
                'status_text' => "未开始",
                'percentage' => $checkItem['percentage'],
            ];

            list($days, $hours) = self::separateManHour($task['man_hour'] * ($checkItem['percentage'] / 100), $apsService->getDayHourLength());
            $planTime = $task['plan_start_time'] + ($days * 24 + $hours) * 3600;
            $tmp['plan_time'] = $planTime;

            if (isset($versionCheckList[$checkItem['id']])) {
                //如果当前版本通过 那么状态为已通过
                $tmp['status_text'] = "审核通过";
            } else if (isset($versionCheckList[$latestKey]) || ($latestKey === 0 && count($versionCheckList) === 0) || isset($versionReviewingCheckList[$key])) {
                //如果上一个版本是已通过  那么当前版本就是进行中
                //如果没有一个版本通过  那么第一个版本就是进行中
                //未审核也是进行中
                $tmp['status_text'] = "进行中";
            } else {
                $tmp['status_text'] = "未开始";
            }
            if (isset($stepReviewWorkflows[$checkItem['step_id']])) {
                $tmp['review_roles'] = $stepReviewWorkflows[$checkItem['step_id']]['review_roles'];
            }

            $result[] = $tmp;
            $latestKey = $key;
        }

        return $result;
    }

    /**
     * 查询任务的全部审核节点
     * @param $taskId
     * @return array|false|mixed|string
     */
    public function getTaskTotalCheckList($taskId)
    {
        $taskData = $this->taskModel
            ->where(['task.id' => $taskId])
            ->join("entity on task.entity_id = entity.id", "LEFT")
            ->join('project on project.id = task.project_id', 'LEFT')
            ->field("task.id,task.step_id,JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.step_workflow_id')) as new_step_workflow_id,entity.step_workflow_id,project.project_template_id")
            ->find();
        $attrStr = $this->getTaskStandardDocAttrStrById($taskId);
        $where = [
            'step_id' => $taskData['step_id'],
            'step_workflow_id' => $taskData['step_workflow_id'],
            'project_template_id' => $taskData['project_template_id'],
            'attr_str' => $attrStr,
        ];
        // 新工序流
        if (!empty($taskData['new_step_workflow_id'])) {
            // 1.去除当前工序流 code
            $nowStepWorkFlowData = model(StepWorkflowModel::class)->where(['id' => $taskData['new_step_workflow_id']])->field('id,code')->find();
            if (empty($nowStepWorkFlowData)) {
                throw_strack_exception("missing new step workflow", ErrorCode::STEP_WORKFLOW_CONFIG_NOT_FOUND);
            }
            // 2.通过 code去匹配出平台审核节点(新)
            $newStepWorkFlowData = model(StepWorkflowModel::class)
                ->where(['code' => $nowStepWorkFlowData['code']])
                ->where(['project_id' => 0, 'project_template_id' => 0])
                ->where(['is_enable' => 'yes'])
                ->field('id')
                ->find();
            $where = [
                'step_id' => $taskData['step_id'],
                'step_workflow_id' => $newStepWorkFlowData['id'] ?? 0,
                'project_template_id' => 0,
                'attr_str' => $attrStr,
            ];
        }
        $returnData = model(CheckListModel::class)
            ->where($where)
            ->order("percentage ASC")->select();

        return $returnData;
    }

    /**
     * 获取任务的版本检查点
     * @param $param
     * @return mixed
     */
    public function getMarkedCheckList($param)
    {
        $taskId = $param['filter']['task_id'];
        $taskModel = model(TaskModel::class);
        $versionModel = model(VersionModel::class);

        $moduleData = Module::$moduleDictData['module_index_by_code']['task'] ?? null;

        if (empty($moduleData)) {
            throw_strack_exception("missing module data", ErrorCode::MISSING_MODULE_PARAM);
        }

        $taskData = $taskModel
            ->where(['task.id' => $taskId])
            ->join("entity on task.entity_id = entity.id", "LEFT")
            ->field("task.id,task.step_id,entity.step_workflow_id")
            ->find();

        if (empty($taskData)) {
            throw_strack_exception('task not found', ErrorCode::TASK_NOT_FOUND);
        }

        // update 2020年10月10日 增加工序流过滤
        $checkListData = $this->getTaskTotalCheckList($taskId);

        $fields = [
            'cl.id',
            'v.link_id',
            'v.module_id',
        ];
        //查询 已通过版本
        $versionList = $versionModel
            ->alias("v")
            ->join("__VERSION_CHECK_LIST__ AS vcl ON v.id = vcl.version_id", "LEFT")
            ->join("__CHECK_LIST__   AS cl ON vcl.check_list_id = cl.id", "LEFT")
            ->where([
                'v.link_id' => $taskId,
                'module_id' => $moduleData['id'],
                'vcl.is_pass' => "yes",
            ])
            ->field($fields)
            ->select();
        $versionList = array_column($versionList, null, 'id');

        foreach ($checkListData as &$checkListItem) {
            $checkListItem['pass'] = false;
            if (isset($versionList[$checkListItem['id']])) {
                $checkListItem['pass'] = true;
            }
        }

        return $checkListData;
    }

    /**
     * 获取多个任务的版本检查点
     * @param $param
     * @return array
     */
    public function getMarkedMultipleCheckList($param)
    {
        $filter = $param['filter'];
        $taskId = $filter['task_ids'];
        $attrStr = $filter['attr_str'] ?? "";
        $projectId = $filter['project_id'];
        $stepWorkflowId = $filter['step_workflow_id'];
        $stepId = $filter['step_id'];

        $moduleData = Module::$moduleDictData['module_index_by_code']['task'] ?? null;

        if (empty($moduleData)) {
            throw_strack_exception("missing module data", ErrorCode::MISSING_MODULE_PARAM);
        }

        $taskData = model(TaskModel::class)->where(['task.id' => ['IN', $taskId]])->field('id')->select();
        $project = model(ProjectModel::class)->field('project_template_id')->find($projectId);
        if (empty($taskData)) {
            throw_strack_exception('task not found', ErrorCode::TASK_NOT_FOUND);
        }

        $checkListData = model(CheckListModel::class)
            ->where([
                'step_id' => $stepId,
                'step_workflow_id' => $stepWorkflowId,
                'project_template_id' => $project['project_template_id'],
                'attr_str' => $attrStr,
            ])
            ->field("id as check_list_id,content")
            ->order("percentage ASC")->select();

        //查询 已通过版本
        $versionList = model(VersionModel::class)
            ->alias("v")
            ->join("__VERSION_CHECK_LIST__ AS vcl ON v.id = vcl.version_id", "LEFT")
            ->join("__CHECK_LIST__   AS cl ON vcl.check_list_id = cl.id", "LEFT")
            ->where([
                'v.link_id' => ['IN', $taskId],
                'module_id' => $moduleData['id'],
                'vcl.is_pass' => "yes",
            ])
            ->field(['cl.id', 'v.link_id', 'v.module_id'])->select();
        $versionList = array_group_by($versionList, 'link_id');
        foreach ($versionList as $index => $versionListItem) {
            $versionList[$index] = array_column($versionListItem, null, 'id');
        }

        $checkData = [];
        foreach ($taskData as $taskDataItem) {
            foreach ($checkListData as $checkListItem) {
                if (!isset($versionList[$taskDataItem['id']][$checkListItem['check_list_id']])) {
                    if (!isset($checkData[$taskDataItem['id']])) {
                        $checkData[$taskDataItem['id']] = $taskDataItem;
                        $checkData[$taskDataItem['id']]['check_list_data'] = [$checkListItem];
                    } else {
                        $checkData[$taskDataItem['id']]['check_list_data'][] = $checkListItem;
                    }
                }
            }
        }
        return array_values($checkData);
    }

    /**
     * 最后一个待审核节点
     * @param $param
     * @return mixed
     */
    public function getLatestCheckItem($param)
    {
        $res = $this->getMarkedCheckList($param);
        $res = array_filter($res, function ($item) {
            return $item['pass'] == false;
        });
        return array_shift($res);
    }

    /**
     * 获取异常任务列表
     * @param $roleCode
     * @param $param
     * @param $currentUserId
     * @return array
     * @throws \Exception
     */
    public function selectAbnormalTaskList($roleCode, $param, $currentUserId)
    {
        // 1.确定当前用户管理任务数据范围
        switch ($roleCode) {
            case 'producer':
                // 制片人：管理的项目下面的数据
                $projectMemberModel = model(ProjectMemberModel::class);
                $projectData = $projectMemberModel->field('project_id')->where(['user_id' => $currentUserId])->select();
                if (!empty($projectData)) {
                    $param['filter'] = append_filter_param($param, 'task', ['project_id' => ['IN', join(',', array_column($projectData, 'project_id'))]]);
                } else {
                    $param['filter'] = append_filter_param($param, 'task', ['project_id' => 0]);
                }
                break;
            case 'director':
                // 导演：管理的集下面的数据
                $episodesData = EpisodeAllocationService::getInstance()->getEpisodesAllocationEntityIds($currentUserId, [1]);
                $directorEntityIds = array_merge($episodesData['episode']['ids'], $episodesData['sequence']['ids'], $episodesData['shot']['ids']);

                $directorEntityFilter = [
                    'entity_module_id' => ['IN', "{$episodesData['episode']['module_id']},{$episodesData['sequence']['module_id']},{$episodesData['shot']['module_id']}"],
                ];
                if (!empty($directorEntityIds)) {
                    $directorEntityFilter['entity_id'] = ['IN', join(',', $directorEntityIds)];
                } else {
                    $directorEntityFilter['entity_id'] = 0;
                }

                $param['filter'] = append_filter_param($param, 'task', $directorEntityFilter);
                break;
            case 'headman':
                // 组长：执行人是自己或者分配人是自己的任务
                $param['filter'] = append_filter_param($param, 'task', [
                    'assignee' => ['eq', $currentUserId],
                    'executor' => ['eq', $currentUserId],
                    "_logic" => "OR",
                ]);
                break;
        }


        // 2.异常任务定义
        // 逾期：当前时间大于任务时间
        // 预警：规则 TODO

        // 3.逾期
        $param['filter'] = append_filter_param($param, 'task', [
            'plan_end_time' => ['LT', time()],
        ]);

        return $this->taskModel->selectData($param);
    }

    /**
     * 任务分类统计
     * @param $param
     * @return array
     */
    public function getTaskCategoryStatistics($param)
    {
        $taskModel = model(TaskModel::class);
        $filter = $param['param']['filter'] ?? [];

        if (isset($filter['project_id']) && (int)$filter['project_id'] <= 0) {
            unset($filter['project_id']);
        }
        /**
         * 正常 进行中 未开始 not_started blocked in_progress done feedback
         * 审核中 daily
         * 异常 计划时间
         *
         */
        // 获取状态id分组通过状态从属关系
        $statusService = StatusService::getInstance();
        $allStatusData = $statusService->getStatusIdGroupByCorrespond();


        $taskNotStartedStatusIds = $allStatusData['not_started'] ?? [];
        $taskBlockedStatusIds = $allStatusData['blocked'] ?? [];
        $taskInProgressStatusIds = $allStatusData['in_progress'] ?? [];
        $taskDoneStatusIds = $allStatusData['done'] ?? [];
        $taskFeedBackStatusIds = $allStatusData['feedback'] ?? [];
        $taskDailyStatusIds = $allStatusData['daily'] ?? [];

        $statusFilter = array_merge($taskNotStartedStatusIds, $taskBlockedStatusIds, $taskInProgressStatusIds, $taskDoneStatusIds, $taskFeedBackStatusIds);

        $statusFilter = implode(',', $statusFilter);

        $normalFilter = array_merge($filter, [
            '_string' => "json_extract(json,'$.task_status') IN ({$statusFilter})",
        ]);

        $dailyStatusFilter = implode(',', $taskDailyStatusIds);
        $dailyFilter = array_merge($filter, [
            '_string' => "json_extract(json,'$.task_status') IN ({$dailyStatusFilter})",
        ]);

        // TODO 警告任务统计规则
        $warningFilter = array_merge($filter, [
            'plan_end_time' => ['LT', strtotime("-1 days")],
        ]);

        $normalStatistics = $taskModel->where($normalFilter)->group("step_category_id")->field("count(id) as count,step_category_id")->select();
        $normalStatistics = array_column($normalStatistics, null, 'step_category_id');

        $dailyStatistics = $taskModel->where($dailyFilter)->group("step_category_id")->field("count(id) as count,step_category_id")->select();
        $dailyStatistics = array_column($dailyStatistics, null, 'step_category_id');

        $warningStatistics = $taskModel->where($warningFilter)->group("step_category_id")->field("count(id) as count,step_category_id")->select();
        $warningStatistics = array_column($warningStatistics, null, 'step_category_id');

        $categoryIdData = array_map(function ($item) {
            return $item['step_category_id'];
        }, $normalStatistics, $dailyStatistics, $warningStatistics);
        $categoryIds = implode(',', array_unique($categoryIdData));
        $stepCategoryModel = model(StepCategoryModel::class);
        $stepCategoryData = $stepCategoryModel->where(['id' => ['IN', $categoryIds]])->field("id,name,code")->select();

        $result = [];
        $normal = [
            "title" => "正常",
            "value" => [],
        ];
        $daily = [
            "title" => "审核中",
            "value" => [],
        ];
        $warning = [
            "title" => "异常",
            "value" => [],
        ];
        $result['categories'] = array_column($stepCategoryData, 'name');

        foreach ($stepCategoryData as $stepCategoryItem) {
            $normal['value'][] = $normalStatistics[$stepCategoryItem['id']]['count'] ?? 0;
            $daily['value'][] = $dailyStatistics[$stepCategoryItem['id']]['count'] ?? 0;
            $warning['value'][] = $warningStatistics[$stepCategoryItem['id']]['count'] ?? 0;
        }
        $result['list'] = [
            $normal, $daily, $warning,
        ];

        return $result;
    }

    /**
     * 获得任务最新的版本
     * @param $taskId
     * @param array $filter
     * @return array|false|string|null
     */
    public function getLatestVersion($taskId, $filter = [])
    {
        $versionModel = model(VersionModel::class);
        $taskModule = Module::$moduleDictData['module_index_by_code']['task'];
        $versionModule = Module::$moduleDictData['module_index_by_code']['version'];
        $res = $versionModel
            ->where(['version.link_id' => $taskId, 'version.module_id' => $taskModule['id']])
            ->where($filter)
            ->join("__REVIEW_REQUEST__ ON review_request.module_id = {$versionModule['id']} AND review_request.link_id = version.id")
            ->field("version.id,version.number,version.is_pass,version.created,review_request.reviewed,
            (select review_request_node.real_reviewer FROM review_request_node WHERE is_reviewed = 'yes' AND review_request_id = review_request.id order by review_request_node.reviewed desc limit 1) AS last_reviewer_user_id,
            (select review_request_node.plan_reviewer FROM review_request_node WHERE is_reviewed = 'no' AND review_request_id = review_request.id order by review_request_node.id  ASC limit 1 ) AS next_reviewer_user_id,
            review_request.is_reviewed
            ")
            ->order("id desc")
            ->find();
        if ($res) {
            if ($res['next_reviewer_user_id'] > 0) {
                $res['current_reviewer'] = model(UserModel::class)->field("json,uuid", true)->find($res['next_reviewer_user_id']);
            }
            if ($res['last_reviewer_user_id'] > 0) {
                $res['last_reviewer'] = model(UserModel::class)->field("json,uuid", true)->find($res['last_reviewer_user_id']);
                $res['reviewer_name'] = !empty($res['last_reviewer']) ? $res['last_reviewer']['name'] : null;
            }
        }
        return $res;
    }

    /**
     * 获取任务的实际工时 包含当前进行中的timelog
     * @param $param
     * @return array
     */
    public function getTaskActiveManHour($param)
    {
        $taskModel = model(TaskModel::class);
        $timeLogModel = model(TimelogModel::class);
        $taskModule = Module::$moduleDictData['module_index_by_code']['task'];

        $task = $taskModel->where($param['filter'])->find();

        if (empty($task)) {
            throw_strack_exception("task not exist", ErrorCode::TASK_NOT_EXIST);
        }

        $doneSec = (int)$timeLogModel->where([
            'complete' => "yes",
            'link_id' => $task['id'],
            'module_id' => $taskModule['id'],
            'end_time' => ["gt", "start_time"],
        ])->sum("end_time-start_time");

        $inProgressStartTime = (int)$timeLogModel->where([
            'complete' => "no",
            'link_id' => $task['id'],
            'module_id' => $taskModule['id'],
        ])->getField("start_time");

        $inProgressSec = 0;
        $is_active = false;
        if ($inProgressStartTime > 0) {
            $inProgressSec = (time() - $inProgressStartTime);
            $is_active = true;
        }
        $totalSecond = $doneSec + $inProgressSec;

        return [
            'total' => $totalSecond,
            'is_active' => $is_active,
        ];
    }

    /**
     * 获取任务的实际工时 包含当前进行中的timelog
     * @param $param
     * @return array
     */
    public function getTaskListActualManHour($param)
    {
        $taskModel = model(TaskModel::class);
        $timeLogModel = model(TimelogModel::class);
        $taskModule = Module::$moduleDictData['module_index_by_code']['task'];

        $res = [];
        $tasks = $taskModel->where($param['filter'])->select();

        if (empty($tasks)) {
            return $res;
        }

        $taskIds = array_column($tasks, 'id');

        $doneTimeLogs = $timeLogModel
            ->where([
                'complete' => "yes",
                'link_id' => ['IN', implode(',', $taskIds)],
                'module_id' => $taskModule['id'],
                'end_time' => ["gt", "start_time"],
            ])->field("link_id,sum(end_time-start_time) as sum_cost")
            ->group("link_id")
            ->select();

        $doneTimeLogs = array_group_by($doneTimeLogs, 'link_id');


        $doneTimeLogs = array_column($doneTimeLogs, 'sum_cost', 'link_id');


        $inProgressTimeLogs = $timeLogModel
            ->where([
                'complete' => "no",
                'link_id' => ['IN', implode(',', $taskIds)],
                'module_id' => $taskModule['id'],
            ])->field("link_id,start_time")
            ->select();
        // 进行中的timelog 每个任务最多只会存在一个进行中
        $inProgressTimeLogs = array_column($inProgressTimeLogs, 'start_time', 'link_id');


        foreach ($doneTimeLogs as $taskId => $timeLog) {
            $res[$taskId] = [
                'task_id' => $taskId,
                'done' => $timeLog,
                'total' => $timeLog,
                'in_progress' => 0,
                'is_active' => false,
            ];
            $inProgressStartTime = $inProgressTimeLogs[$taskId] ?? null;
            if (!empty($inProgressStartTime) && $inProgressStartTime > 0) {
                $inProgressSec = (time() - $inProgressStartTime);
                $res[$taskId]['is_active'] = true;
                $res[$taskId]['in_progress'] = $inProgressSec;
                $res[$taskId]['total'] += $inProgressSec;
            }
        }
        return $res;
    }

    /**
     * 获得任务的父级任务
     * @param $taskId
     * @param string $parentStepType
     * @return array|null
     */
    public function getParentTask($taskId, $projectId, $parentStepType = 'step_category'): ?array
    {
        $returnData = model(TaskRelationModel::class)
            ->setSuffix(sprintf('_project_%d', $projectId))
            ->alias('task_relation')
            ->join('task ON task_relation.source_task_id = task.id', 'RIGHT')
            ->where([
                'task_relation.target_task_id' => $taskId,
                'task.step_type' => $parentStepType,
            ])
            ->field("task.*")
            ->find();
        return $returnData;
    }

    /**
     * 获得任务的子级任务
     * @param $taskId
     * @return array|null
     */
    public function getChildrenTasks($taskId)
    {
        $taskModel = model(TaskModel::class);
        return $taskModel
            ->join("task_relation  ON task_relation.target_task_id = task.id", "LEFT")
            ->where(['task_relation.source_task_id' => $taskId])
            ->field("task.*")
            ->select();
    }

    /**
     * 获得任务的所属的集
     * @param $taskId
     * @return array | null
     */
    public function getTaskEpisode($taskId)
    {
        $data = model(EntityModel::class)
            ->join("task ON task.entity_id = entity.id", "LEFT")
            ->where(['task.id' => $taskId])
            ->field("entity.initial_episode_code,entity.project_id")
            ->find();
        if (empty($data)) {
            return $data;
        }
        return model(EntityModel::class)->where(['code' => $data['initial_episode_code'], 'module_id' => module_by_code('episode')['id'], 'project_id' => $data['project_id']])->find();
    }

    /**
     * 指定实体 更新实体关联的工序分类任务
     * @param $param
     * @return array
     * @throws Exception
     * @throws Throwable
     */
    public function updateTaskDateByEntity($param)
    {
        /**
         *
         * 通过修改实体开始时间 结束时间来
         *
         * 修改实体下面工序分类任务的计划时间
         *
         */
        $entityId = $param['entity_id'];
        $startTime = strtotime($param['start_time']);
        $endTime = strtotime($param['end_time']);
        $planDuration = number_floor_precision(($endTime - $startTime) / 3600, 2);

        $entityData = model(EntityModel::class)
            ->where(['id' => $entityId])
            ->find();

        if (empty($entityData)) {
            throw new LogicException("missing match entity data [{$entityId}]", ErrorCode::MISSING_MATCH_ENTITY_DATA);
        }
        $episode = model(EntityModel::class)->where(['code' => $entityData['initial_episode_code'], 'project_id' => $entityData['project_id'], 'module_id' => Module::$moduleDictData['module_index_by_code']['episode']['id']])->find();
        if (empty($episode)) {
            throw new LogicException('episode not found', ErrorCode::MISSING_EPISODE);
        }
        $episodePlan = PlanService::getInstance()->getPlan(['entity_id' => $episode['id'], 'type' => 'episode'], 'start_time,end_time');

        $entityModel = model(EntityModel::class);
        $apsService = new ApsService();
        $entityModel->startTrans();

        try {
            if ($entityData['cost_hours'] != $planDuration) {
                // 如果更新了实体工时 那么更新实体的工时
                model(EntityModel::class)->where(['id' => $entityId])->setField('cost_hours', $planDuration);
            }
            $entityModel->save([
                'id' => $entityId,
                'plan_start_time' => $startTime,
                'plan_end_time' => $endTime,
            ]);

            $taskList = model(TaskModel::class)
                ->join("step_category ON step_category.id = task.step_category_id", "LEFT")
                ->where([
                    'task.entity_id' => $entityId,
                    'task.step_type' => 'step_category',
                    'task.type' => 'normal',
                    'task.project_id' => $entityData['project_id'],
                ])
                ->field("task.id,task.name,task.man_hour,step_category.parent_id as step_category_parent_id")
                ->order("task.pre_task_id ASC")
                ->select();

            // 查询出父级分类计划
//            $stepCategoryParentIdData = array_column($taskList, 'step_category_parent_id', 'step_category_parent_id');

            // 查询出工序任务
            $taskIdArray = array_column($taskList, "id");
            $stepTasks = model(TaskModel::class)->join("task_relation  ON task_relation.target_task_id = task.id", "LEFT")
                ->where(['task_relation.source_task_id' => ['in', implode(',', $taskIdArray)]])
                ->field("task.*,task_relation.source_task_id")
                ->select();

            $stepTasks = array_column($stepTasks, null, 'id');
            $totalManHour = (float)array_sum(array_column($stepTasks, 'man_hour'));

            $stepTasks = array_group_by($stepTasks, 'source_task_id');
            $stepTasks = array_map(function ($steps) use (&$totalManHour) {
                return array_sort_by($steps, 'pre_task_id');
            }, $stepTasks);

            $data = [];
            $planStartTime = $startTime;
            foreach ($taskList as $task) {
                if ($totalManHour > 0) {
                    // 利用工时占比 计算时间长度
                    $planEndTime = $planStartTime + ((float)$task['man_hour'] / $totalManHour) * ($planDuration * 3600);
                } else {
                    $planEndTime = $planStartTime + 0;
                }
                if ($planStartTime < $episodePlan['start_time']
                    || $planEndTime > $episodePlan['end_time']
                ) {
                    $startDate = get_format_date($episodePlan['start_time'], 1);
                    $endDate = get_format_date($episodePlan['end_time'], 1);
                    $planStartDate = get_format_date($planStartTime, 1);
                    $planEndDate = get_format_date($planEndTime, 1);
                    throw new LogicException("task [{$task['name']}] {$task['step_category_parent_id']} plan date {$planStartDate}-{$planEndDate} should be {$startDate}-{$endDate}", ErrorCode::TASK_PLAN_TIME_MUST_BE_WITHIN_THE_SPECIFIED_RANGE);
                }
                $data[] = model(TaskModel::class)->save([
                    'id' => $task['id'],
                    'plan_end_time' => $planEndTime,
                    'plan_start_time' => $planStartTime,
                    'is_manual_adjustment' => "yes",
                ]);

                // 更新工序任务开始,结束时间
                $currentStepTasks = $stepTasks[$task['id']] ?? [];
                $startTime = $planStartTime;
                foreach ($currentStepTasks as $stepTask) {
                    list($days, $hours) = TaskService::separateManHour($stepTask['man_hour'], $apsService->getDayHourLength());
                    $endTime = $startTime + ($days * 24 + $hours) * 3600;
                    $tmpUpdateData = [
                        'id' => $stepTask['id'],
                        'plan_start_time' => $startTime,
                        'plan_end_time' => $endTime,
                        'is_manual_adjustment' => "yes",
                    ];
//                    $res['update_data'][] = $tmpUpdateData;

                    $data[] = model(TaskModel::class)->save($tmpUpdateData);
                    $startTime = $endTime;
                }
                $planStartTime = $planEndTime;
            }
            $entityModel->commit();
            return $data;
        } catch (Throwable $e) {
            $entityModel->rollback();
            throw $e;
        }
    }

    /**
     * 获得未通过的审核点
     * @param $param
     * @return array
     */
    public function getUndoCheckItem($param)
    {
        $res = $this->getMarkedCheckList($param);
        $res = array_filter($res, function ($item) {
            return $item['pass'] == false;
        });
        return array_values($res);
    }

    /**
     * 获取分组的任务列表
     * @param $param
     * @param int $userId
     * @param $currentModuleCode
     * @param bool $firstActive 是否要求激活的任务在首位
     * @return array
     * @throws \Exception
     */
    public function getTaskListGrouped($param, int $userId, $currentModuleCode, $firstActive = true)
    {
        $groupBy = $param['group_by'] ?? "step";
        // 默认字段
        $fields = [
            "task.id",
            "task.name",
            "task.code",
            "task.man_hour",
            "task.frame_range",
            "task.timecode",
            "task.type",
            "task.step_type",
            "task.step_category_id",
            "task.step_id",
            "task.entity_id",
            "task.plan_start_time",
            "task.plan_end_time",
            "task.plan_duration",
            "task.description",
            "task.assignee",
            "task.executor",
            "task.created_by",
            "task.created",
            "task.actual_man_hour",
            "task_tag.id",
            "task_tag.name",
            "task_tag.color",
            "task_status.id",
            "task_status.name",
            "task_status.code",
            "task_status.color",
            "entity.id",
            "entity.name",
            "entity.code",
            "entity.json",
            "entity.initial_episode_code",
            "entity.project_id",
            "entity.module_id",
            "entity.entity_id",
            "entity.entity_module_id",
            "module.id",
            "module.name",
            "module.code",
            "project.id",
            "project.name",
            "project.code",
            "project.type",
        ];
//        $indexKey = "";
        switch ($groupBy) {
            case "entity":
                $indexKey = "entity_id";
                break;
            case "step":
            default:
                $indexKey = "step_id";
        }
        //支持外部字段追加
        if (empty($param['fields'])) {
            $param['fields'] = $fields;
        } else {
            $param['fields'] = array_merge($fields, explode(',', $param['fields']));
        }
        $param['fields'] = implode(',', $param['fields']);
        if (empty($param['page'])) {
            // 默认分页数量
            $param['page'] = [1, C("database.database_max_select_rows")];
        }
        // 全部项目 工作台移除空project_id
        if (isset($param['filter']['task.project_id']) && empty($param['filter']['task.project_id'])) {
            unset($param['filter']['task.project_id']);
        }
        $param['filter']['task.entity_id'] = ['GT', 0];
        // 增加当前用户 executor id
        $param['filter'] = append_filter_param($param, $currentModuleCode, ['executor' => $userId]);
        // 增加任务类型为step
        $param['filter'] = append_filter_param($param, $currentModuleCode, ['step_type' => 'step']);
        $commonService = CommonService::instance('task');
        $timelogTaskId = 0;
        if ($firstActive) {
            // 排除当前时间日志
            $timelogTaskData = TimelogService::getInstance()->getPersonalTimer($userId, 'task');
            $activeTaskItem = null;
            if (!empty($timelogTaskData)) {
                $timelogTaskId = $timelogTaskData['link_id'];
                $taskModel = model(TaskModel::class);
                $firstParam = $param;
                $firstParam['filter']['task.id'] = $timelogTaskId;
                $activeTaskItem = $taskModel->findData($firstParam);
                $param['filter'] = append_filter_param($param, $currentModuleCode, ['id' => ['neq', $timelogTaskId]]);
            }
        }

        $taskData = $commonService->select($param, $currentModuleCode);

        if ($firstActive && !empty($activeTaskItem)) {
            $activeTaskItem['time_log'] = $timelogTaskData;
            array_unshift($taskData['rows'], $activeTaskItem);
            $taskData['total'] += 1;
        }
        $resData = $taskData;
        if ($taskData['total'] > 0) {
            // 获取查询ids
            $filterStepCategoryIds = [];
            $filterStepIds = [];
            $filterUserIds = [];
            $filterEntityIds = [];
            $filterEpisodes = [];
            foreach ($taskData['rows'] as &$row) {
                $row['project_id'] = $row['entity']['project_id'];
                $row['task_id'] = $row['task']['id'];
                $row['entity_id'] = $row['task']['entity_id'];
                $row['step_id'] = $row['task']['step_id'];
                $row['step_category_id'] = $row['task']['step_category_id'];
                if (!empty($row['task']['step_category_id']) && !in_array($row['task']['step_category_id'], $filterStepCategoryIds)) {
                    $filterStepCategoryIds[] = $row['task']['step_category_id'];
                }
                if (!empty($row['task']['step_id']) && !in_array($row['task']['step_id'], $filterStepIds)) {
                    $filterStepIds[] = $row['task']['step_id'];
                }
                if (!empty($row['task']['assignee']) && !in_array($row['task']['assignee'], $filterUserIds)) {
                    $filterUserIds[] = $row['task']['assignee'];
                }
                if (!empty($row['task']['executor']) && !in_array($row['task']['executor'], $filterUserIds)) {
                    $filterUserIds[] = $row['task']['executor'];
                }
                if (!empty($row['task']['created_by']) && !in_array($row['task']['created_by'], $filterUserIds)) {
                    $filterUserIds[] = $row['task']['created_by'];
                }
                if (!empty($row['task']['entity_id']) && !in_array($row['task']['entity_id'], $filterEntityIds)) {
                    $filterEntityIds[] = $row['task']['entity_id'];
                }
                if ($row['entity']['entity_id'] > 0 && !in_array($row['entity']['entity_id'], $filterEntityIds)) {
                    $filterEntityIds[] = $row['entity']['entity_id'];
                }
                if (!empty($row['entity']['initial_episode_code']) && (!array_key_exists($row['project_id'], $filterEpisodes) || !in_array($row['entity']['initial_episode_code'], $filterEpisodes[$row['project_id']]))) {
                    $filterEpisodes[$row['project_id']][] = $row['entity']['initial_episode_code'];
                }
                $row['latest_version'] = $this->getLatestVersion($row['task']['id']);
            }
            $entityMap = [];
            // 实体数据查询
            if (!empty($filterEntityIds)) {
                $entityMap = model(EntityModel::class)
                    ->field('id,name,code,initial_episode_code,json,entity_id,entity_module_id,module_id')
                    ->where(['id' => ['IN', implode(',', $filterEntityIds)]])
                    ->select();
                $entityMap = array_column($entityMap, null, 'id');
                $entityMap = array_map(function ($item) {
                    $item = array_merge($item, json_decode($item['json'], true) ?? []);
                    unset($item['json']);
                    return $item;
                }, $entityMap);
            }
            // 集查询
            $episodeMap = [];
            if (count($filterEpisodes) > 0) {
                $episodeQueryFilter = [];
                foreach ($filterEpisodes as $filterEpisodeProjectId => $filterEpisodeCodes) {
                    $episodeQueryFilter[] = [
                        "project_id" => $filterEpisodeProjectId,
                        "code" => ["IN", implode(',', $filterEpisodeCodes)],
                    ];
                }
                $episodeQueryFilter["_logic"] = "OR";
                $episodeMap = model(EntityModel::class)
                    ->field("id,name,code,project_id")
                    ->where([
                        $episodeQueryFilter,
                        'module_id' => Module::$moduleDictData['module_index_by_code']['episode']['id'],
                        '_logic' => 'AND',
                    ])
                    ->select();
                // 集以项目进行分组
                $episodeMap = array_group_by($episodeMap, 'project_id');
                $episodeMap = array_map(function ($episodeList) {
                    return array_column($episodeList, null, 'code');
                }, $episodeMap);
            }
            // 填充 step
            $stepDict = [];
            if (!empty($filterStepIds)) {
                $stepModel = model(StepModel::class);
                $stepData = $stepModel->field('id,name,code,color')->where(['id' => ['IN', join(',', $filterStepIds)]])->select();
                $stepDict = array_column($stepData, null, 'id');
            }
            // 填充 step_category
            $stepCategoryDict = [];
            if (!empty($filterStepCategoryIds)) {
                $stepCategoryModel = model(StepCategoryModel::class);
                $stepCategoryData = $stepCategoryModel->field('id,name,code')->where(['id' => ['IN', join(',', $filterStepCategoryIds)]])->select();
                $stepCategoryDict = array_column($stepCategoryData, null, 'id');
            }
            // 填充用户信息
            $userDict = [];
            if (!empty($filterUserIds)) {
                $userModel = model(UserModel::class);
                $userData = $userModel->field('id,name,phone,union_id,avatar')->where(['id' => ['IN', join(',', $filterUserIds)]])->select();
                $userDict = array_column($userData, null, 'id');
            }
            $indexDataMap = [];
            // 数据回填
            foreach ($taskData['rows'] as &$taskItem) {
                $taskItem['step_category'] = $stepCategoryDict[$taskItem['task']['step_category_id']] ?? ['id' => null, 'name' => null, 'code' => null];
                $taskItem['step'] = $stepDict[$taskItem['task']['step_id']] ?? ['id' => null, 'name' => null, 'code' => null];
                $taskItem['assignee'] = $userDict[$taskItem['task']['assignee']] ?? ['id' => null, 'name' => null, 'code' => null];
                $taskItem['executor'] = $userDict[$taskItem['task']['executor']] ?? ['id' => null, 'name' => null, 'phone' => null, 'union_id' => null];

                $taskItem['created_by'] = $userDict[$taskItem['task']['created_by']] ?? ['id' => null, 'name' => null, 'phone' => null, 'union_id' => null];

                $taskItem['entity'] = $entityMap[$taskItem['entity_id']] ?? null;

                if (!empty($taskItem['entity']) && $taskItem['entity']['module_id'] > 0 && $taskItem['entity']['module_id'] > 0 && isset(Module::$moduleDictData['module_index_by_id'][$taskItem['entity']['module_id']])) {
                    $taskItem[Module::$moduleDictData['module_index_by_id'][$taskItem['entity']['module_id']]['code']] = $taskItem['entity'];
                }

                $taskItem['task']['is_active'] = $taskItem['task']['id'] == $timelogTaskId;

                $taskItem['episode'] = $episodeMap[$taskItem['project_id']][$taskItem['entity']['initial_episode_code']] ?? ['id' => null, 'name' => null, 'code' => null,];
                $relationModule = Module::$moduleDictData['module_index_by_id'][$taskItem['entity']['entity_module_id']] ?? null;
                if (!empty($relationModule)) {
                    $taskItem[$relationModule['code']] = $entityMap[$taskItem['entity']['entity_id']] ?? null;
                }
                $tagsTmp = [];
                if (!empty($taskItem['task_status'])) {
                    $tagsTmp[] = array_merge(['tag_type' => "task_status"], $taskItem['task_status']);
                }
                if (!empty($taskItem['task_tag'])) {
                    $tags = array_map(function ($item) {
                        $item['tag_type'] = 'task_tag';
                        return $item;
                    }, $taskItem['task_tag']);
                    $tagsTmp = array_merge($tagsTmp, $tags);
                }
                if (!empty($taskItem['step'])) {
                    $tagsTmp[] = array_merge(['tag_type' => "step"], $taskItem['step']);
                }
                //最新版本
                if (!empty($taskItem['latest_version'])) {
                    $tagsTmp[] = [
                        'id' => $taskItem['latest_version']['id'],
                        'code' => "",
                        'name' => "v" . $taskItem['latest_version']['number'],
                        'tag_type' => "task_version",
                    ];
                }
                $taskItem['tags'] = $tagsTmp;
                // 组装二层数据map
                if (!empty($taskItem[$groupBy]) && !isset($indexDataMap[$taskItem[$groupBy]['id']])) {
                    $indexDataMap[$taskItem[$groupBy]['id']] = $taskItem[$groupBy];
                }
            }
            // 第一层分组使用 二级分类id
            $taskData['rows'] = array_group_by($taskData['rows'], 'step_category_id');
            // 第二层分组 通过indexKey
            $taskData['rows'] = array_map(function ($items) use ($indexKey) {
                return array_group_by($items, $indexKey);
            }, $taskData['rows']);
            $resData['rows'] = [];
            unset($row);
            foreach ($taskData['rows'] as $k1 => $row) {
                $tmp = $stepCategoryDict[$k1];
//                $indexKeyList = array_keys($row);
                foreach ($row as $indexKeyItem => $indexKeyValue) {
                    if (!isset($indexDataMap[$indexKeyItem])) {
                        continue;
                    }
                    $tmp['children'][] = array_merge($indexDataMap[$indexKeyItem], ['children' => $indexKeyValue]);
                }
//                foreach ($indexKeyList as $indexKeyItem) {
//                    if (!isset($indexDataMap[$indexKeyItem])) {
//                        continue;
//                    }
//                    $tmp['children'][] = array_merge($indexDataMap[$indexKeyItem], ['children' => $row[$indexKeyItem]]);
//                }
                $resData['rows'][] = $tmp;
            }
            $resData['total'] = $taskData['total'];
        }
        return $resData;
    }

    /**
     * 执行中任务列表 通过分类分组
     * @param $param
     * @param int $userId
     * @param $currentModuleCode
     * @return array
     * @throws \Exception
     */
    public function getExecutingTaskListGrouped($param, int $userId, $currentModuleCode)
    {
        $statusService = StatusService::getInstance();
        $doneStatus = $statusService->getTaskDoneStatus();
        // 当用户指定状态筛选  那么就不筛选未完成状态
        if (!array_key_exists('task.task_status', $param['param']['filter'])) {
            $param['param']['filter'] = ['task_status.id' => ['neq', $doneStatus['id']]];
        }
        return $this->getTaskListGrouped($param['param'], $userId, $currentModuleCode);
    }

    /**
     * 已完成任务列表 通过分类分组
     * @param $param
     * @param int $userId
     * @param $currentModuleCode
     * @return array
     * @throws \Exception
     */
    public function getDoneTaskListGrouped($param, int $userId, $currentModuleCode)
    {
        $statusService = StatusService::getInstance();
        $doneStatus = $statusService->getTaskDoneStatus();
        $param['param']['filter']['task.task_status'] = $doneStatus['id'];
        return $this->getTaskListGrouped($param['param'], $userId, $currentModuleCode);
    }

    /**
     * 查询待分配的任务列表 通过分类分组
     * @param $param
     * @param int $userId
     * @param $tenantId
     * @return array
     * @deprecated 2021年5月22日
     */
    public function getStepTaskWaitForAssignGroupByStepCategory($param, int $userId, $tenantId): array
    {
        $groupBy = $param['group_by'] ?? "step";
        $isAssign = $param['is_assigned'] ?? 'no';
        $filter = $param['filter'] ?? [];
        unset($filter['entity.name']);

        $taskModel = model(TaskModel::class);
        $queryField = [
            "task.id",
            "task.name",
            "task.code",
            "task.type",
            "task.man_hour",
            "task.actual_man_hour",
//            "task.type as type",
            "task.plan_start_time as start_time",
            "task.plan_end_time as end_time",
            "task.plan_duration as duration",
            "task.entity_module_id as module_id",
            "task.step_type",
            "task.step_id",
            "task.step_category_id",
            "task.project_id",
            "task.executor",
            "task.assignee",
            "task.entity_id",
            "task.entity_module_id",
            "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status')) as task_status",
            "task.pre_task_id as pre_task_id",

        ];

        $entityQueryField = [
            "id",
            "name",
            "code",
            "json",
            "project_id",
            "initial_episode_code",
            "entity_id",
            "entity_module_id",
        ];

        $entitySearchFilter = [
            'tenant_id' => $tenantId,
        ];
        $filter = array_merge($filter, [
            'task.assignee' => $userId,
            'task.type' => "sub",
            'task.step_type' => "step",
            'task.tenant_id' => $tenantId,
            "_logic" => "AND",
        ]);
        if ($isAssign === 'yes') {
            $filter[] = [
                'task.is_assignee' => 'yes',
                'task.executor' => ['GT', 0],
            ];
        } else {
            $filter[] = [
                'task.is_assignee' => 'no',
                'task.executor' => 0,
            ];
        }

//        $indexKey = "";
        switch ($groupBy) {
            case "entity":
                $indexKey = "entity_id";
                break;
            case "step":
            default:
                $indexKey = "step_id";
        }
        $entityQuery = $taskModel
            ->field($queryField);

        if (isset($param['filter']['project_id']) && $param['filter']['project_id'] > 0) {
            $filter['task.project_id'] = (int)$param['filter']['project_id'];
            $entitySearchFilter['project_id'] = (int)$param['filter']['project_id'];
        }
        // 实体名称搜索
        if (isset($filter['entity.name'])) {
            $entitySearchFilter['name'] = ['LK', $filter['entity.name']];
            $entityIdList = model(EntityModel::class)->where($entitySearchFilter)->field('id')->select();
            if (count($entityIdList) > 0) {
                $filter['task.entity_id'] = ['IN', implode(',', array_column($entityIdList, 'id'))];
            }
        }
        // 状态筛选
        if (!empty($filter['task.task_status'])) {
            $filter["JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status'))"] = $filter['task.task_status'];
            unset($filter['task.task_status']);
        }


        $tasks = $entityQuery->where($filter)->field($queryField)->select();
        $tasks = array_column($tasks, null, 'id');
        // 查询用户信息
        $userIdsExecutor = array_filter(array_column($tasks, 'executor', 'executor'), function ($item) {
            return intval($item) > 0;
        });
        $userIdsAssignee = array_filter(array_column($tasks, 'assignee', 'assignee'), function ($item) {
            return intval($item) > 0;
        });
        $userIds = array_unique(array_merge($userIdsExecutor, $userIdsAssignee));
        $userModel = model(UserModel::class);
        $users = $userModel->where(['id' => ['IN', join(',', $userIds)]])->field("id,name,email")->select();
        $users = array_column($users, null, 'id');

        //查询关联的实体信息
        $entityIds = array_column($tasks, 'entity_id', 'entity_id');

        $entityModel = model(EntityModel::class);
        $entityData = $entityModel
            ->where(['id' => ['IN', implode(',', $entityIds)]])
            ->field($entityQueryField)
            ->select();
        $entityData = array_column($entityData, null, 'id');

        // 实体的父级实体信息查询
        $parentEntityIdList = array_column($entityData, 'entity_id', 'entity_id');
        if (count($parentEntityIdList) > 0) {
            $parentEntityData = $entityModel
                ->where(['id' => ['IN', implode(',', $parentEntityIdList)]])
                ->field($entityQueryField)
                ->select();
            $parentEntityData = array_column($parentEntityData, null, 'id');
            $entityData += $parentEntityData;
        }

        // 集筛选条件组装
        $filterEpisodes = [];
        $entityDataGroupedByProject = array_group_by($entityData, 'project_id');
        foreach ($entityDataGroupedByProject as $projectId => $tmpEntityList) {
            $episodeCodes = array_column($tmpEntityList, 'initial_episode_code', 'initial_episode_code');
            $episodeCodes = array_filter($episodeCodes, function ($epCode) {
                return !empty($epCode);
            });
            if (count($episodeCodes) > 0) {
                $filterEpisodes[$projectId] = $episodeCodes;
            }
        }

        // 集查询
        $episodeMap = [];
        if (count($filterEpisodes) > 0) {
            $episodeQueryFilter = [];
            foreach ($filterEpisodes as $filterEpisodeProjectId => $filterEpisodeCodes) {
                $episodeQueryFilter[] = [
                    "project_id" => $filterEpisodeProjectId,
                    "code" => ["IN", implode(',', $filterEpisodeCodes)],
                ];
            }
            $episodeQueryFilter["_logic"] = "OR";
            $episodeMap = model(EntityModel::class)
                ->field("id,name,code,project_id")
                ->where([
                    $episodeQueryFilter,
                    'module_id' => Module::$moduleDictData['module_index_by_code']['episode']['id'],
                    '_logic' => 'AND',
                ])
                ->select();
            // 集以项目进行分组
            $episodeMap = array_group_by($episodeMap, 'project_id');
            $episodeMap = array_map(function ($episodeList) {
                return array_column($episodeList, null, 'code');
            }, $episodeMap);
        }

        //查询项目信息
        $projectIds = array_column($tasks, 'project_id', 'project_id');
        $projectService = ProjectService::getInstance();
        $projectData = $projectService->getProjects(['id' => ['IN', implode(',', $projectIds)]], "id,name,code,belong_project_id");
        $projectData = array_column($projectData, null, 'id');

        //查询状态信息
        $statusIds = array_column($tasks, 'task_status', 'task_status');
        $statusModel = model(StatusModel::class);
        $statusData = [];
        if (!empty($statusIds)) {
            $statusData = $statusModel
                ->where(['id' => ['IN', implode(',', $statusIds)]])
                ->field("id,name,color,code,correspond,icon")
                ->select();
            $statusData = array_column($statusData, null, 'id');
        }

        // 查询分类信息
        $stepCategoryIdData = array_column($tasks, 'step_category_id', 'step_category_id');
        $stepCategoryMap = [];
        if (!empty($stepCategoryIdData)) {
            $stepCategoryMap = model(StepCategoryModel::class)
                ->where(['id' => ['IN', implode(',', $stepCategoryIdData)]])
                ->select();
            $stepCategoryMap = array_column($stepCategoryMap, null, 'id');
        }
        $stepIdData = array_column($tasks, 'step_id', 'step_id');
        $stepMap = [];
        if (!empty($stepIdData)) {
            $stepMap = model(StepModel::class)
                ->where(['id' => ['IN', implode(',', $stepIdData)]])
                ->select();
            $stepMap = array_column($stepMap, null, 'id');
        }
        $indexDataMap = [];
        //填充关联的数据
        foreach ($tasks as &$task) {
            $task['executor'] = $users[$task['executor']] ?? null;
            $task['assignee'] = $users[$task['assignee']] ?? null;
            $task['entity'] = $entityData[$task['entity_id']] ?? null;
            $task['module'] = Module::$moduleDictData['module_index_by_id'][$task['entity_module_id']] ?? null;
            if (!empty($task['entity'])) {
                // 当前任务实体的父级实体信息
                $parentEntityModule = Module::$moduleDictData['module_index_by_id'][$task['entity']['entity_module_id']] ?? null;
                if (!empty($parentEntityModule)) {
                    $task[$parentEntityModule['code']] = $entityData[$task['entity']['entity_id']] ?? null;
                }
                $task['entity_name'] = $task['entity']['name'];
                $task = array_merge($task, json_decode($task['entity']['json'], true) ?? []);
                if (isset($episodeMap[$task['project_id']][$task['entity']['initial_episode_code']])) {
                    $task['episode'] = $episodeMap[$task['project_id']][$task['entity']['initial_episode_code']];
                } else {
                    $task['episode'] = null;
                }
            }

            $task['project'] = $projectData[$task['project_id']] ?? null;
            $task['task_status'] = $statusData[$task['task_status']] ?? null;
            $task['step'] = $stepMap[$task['step_id']] ?? null;
            $task['step_category'] = $stepCategoryMap[$task['step_category_id']] ?? null;
            // 组装二层数据map
            if (!empty($task[$groupBy]) && !isset($indexDataMap[$task[$groupBy]['id']])) {
                $indexDataMap[$task[$groupBy]['id']] = $task[$groupBy];
            }
        }

        // 第一层用工序分类分组
        $tasks = array_group_by($tasks, 'step_category_id');

        // 第二层用户指定group_by 来进行分组
        $tasks = array_map(function ($item) use ($indexKey) {
            return array_group_by($item, $indexKey);
        }, $tasks);

        $resData = [];

        // 通过工序分类进行分组的任务数据
        foreach ($tasks as $k1 => $row) {
            $tmp = $stepCategoryMap[$k1];
            $tmp['real_id'] = $tmp['id'];
            $tmp['id'] = 'step_category' . $tmp['id'];
            // 中间层 数据 entity 或者是step
            foreach ($row as $indexKeyItem => $indexKeyValue) {
                if (!isset($indexDataMap[$indexKeyItem])) {
                    continue;
                }
                $tmpIndexData = $indexDataMap[$indexKeyItem];
                $tmpIndexData['real_id'] = $tmpIndexData['id'];
                $tmpIndexData['id'] = $indexKey . $tmpIndexData['id'];
                $tmp['children'][] = array_merge($tmpIndexData, ['children' => $indexKeyValue]);
            }
            $resData[] = $tmp;
        }
        return $resData;
    }

    /**
     * 审片室 当前版本任务审核点
     * @param $param
     * @return mixed
     */
    public function getTaskVersionCheckList($param)
    {
        $versionId = $param['filter']['version_id'];
        $reviewRequestNodeId = $param['filter']['review_request_node_id'];
        // 所有的审核节点 带通过状态 bool pass
        $checkList = $this->getMarkedCheckList($param);
        // 当前审核人
        $reviewRequestNode = model(ReviewRequestNodeModel::class)->find($reviewRequestNodeId);
        // 当前版本的审核节点
        $versionCheckList = model(VersionCheckListModel::class)
            ->where(['version_id' => $versionId])
            ->select();
        $vId = array_column($versionCheckList, 'check_list_id');
        $vCheckList = array_column($versionCheckList, null, 'check_list_id');
        foreach ($checkList as &$checkItem) {
            $checkItem['is_current_version'] = in_array($checkItem['id'], $vId);

            //  是否已审核 默认等于通过状态
            $checkItem['is_reviewed'] = $checkItem['pass'];

            // 当是当前版本 并且有审核人记录的时候 那么应该为当前审核人的审核状态
            if ($checkItem['is_current_version'] && !empty($reviewRequestNode)) {
                // 版本审核点已经通过 判断当前审核人 是不是重复审核
                $checkItem['is_reviewed'] = $reviewRequestNode['is_reviewed'] === "yes";
            }

            if ($checkItem['is_current_version']) {
                $checkItem['version_check_list'] = $vCheckList[$checkItem['id']];
            }
            //  如果未通过 那么移除标识 避免前端默认选择不通过
            if ($checkItem['pass'] == false) {
                $checkItem['pass'] = null;
            }
        }
        return $checkList;
    }

    /**
     * 检查任务状态
     * @return  int
     * @throws \Exception
     */
    public function checkTaskStatus()
    {
        $status = model(StatusModel::class)
            ->where([
                "correspond" => ["IN", "in_progress,daily,feedback"],
            ])
            ->select();
        $statusIdArray = array_column($status, 'id');
        $statusIds = implode(',', $statusIdArray);
        $projects = model(ProjectModel::class)->where("json_unquote(json_extract(json,'$.project_status')) IN ({$statusIds}) ")->select();
        $warningTaskIdArray = $outDateTaskIdArray = [];

        $detailMessage = [];

        foreach ($projects as $project) {
            $filter = [
                'task.project_id' => $project['id'],
                'task.step_type' => "step",
                'task.executor' => ["gt", 0],
            ];
            $groupKey = "task.id";
            $fields = 'task.id,task.entity_id,task.name,task.assignee,task.executor,task.step_type,task.plan_end_time,task.assignee_at,task.start_time,task.end_time,task.project_id,entity.name as entity_name';
            list($outOfDateTask, $warningTask, ,) = ProjectOkrService::getStatusTask($filter, $groupKey, $fields);
            if (count($warningTask)) {
                $assignUserIdArray = array_column($warningTask, 'assignee', 'assignee');
                $executorUserIdArray = array_column($warningTask, 'executor', 'executor');
                $userIdArray = array_unique(array_merge($assignUserIdArray, $executorUserIdArray));
                $users = model(UserModel::class)->where(["id" => ["in", implode(',', $userIdArray)]])->field("id,name,union_id")->select();
                $users = array_column($users, null, 'id');

                foreach ($warningTask as $task) {
                    $warningTaskIdArray[] = $task['id'];
                    $executor = $users[$task['executor']] ?? null;

                    $notifyParam = [
                        'task_name' => $task['entity_name'] . '-' . $task['name'],
                        'step_name' => $task['name'],
                        'plan_end_time' => $task['plan_end_time'],
                        'executor_name' => $executor['name'] ?? "",
                        'extra_data' => [
                            'task_id' => $task['id'],
                            'entity_id' => $task['entity_id'],
                        ],
                    ];
                    // 任务执行人
                    if (isset($users[$task['executor']])) {
                        $detailMessage[$executor['union_id']]['warning'][] = [
                            "link_type" => "task",
                            'link_id' => $task['id'],
                            'type' => 'warning',
                            'message_content' => $notifyParam,
                        ];
                    }

                    // 任务分配人
                    if (isset($users[$task['assignee']])) {
                        $assignUser = $users[$task['assignee']];
                        $detailMessage[$assignUser['union_id']]['warning'][] = [
                            "link_type" => "task",
                            'link_id' => $task['id'],
                            'type' => 'warning',
                            'message_content' => $notifyParam,
                        ];
                    }

                    // 父级任务分配人
                    $parentTask = $this->getParentTask($task['id'], $task['project_id']);
                    if ($parentTask) {
                        $parentTaskAssignUser = model(UserModel::class)->field("union_id")->cache(60)->find($parentTask['assignee']);
                        if (!empty($parentTaskAssignUser)) {
                            $detailMessage[$parentTaskAssignUser['union_id']]['warning'][] = [
                                "link_type" => "task",
                                'link_id' => $task['id'],
                                'type' => 'warning',
                                'message_content' => $notifyParam,
                            ];
                        }
                    }
                }
            }
            if (count($outOfDateTask)) {
                $assignUserIdArray = array_column($outOfDateTask, 'assignee', 'assignee');
                $executorUserIdArray = array_column($outOfDateTask, 'executor', 'executor');
                $userIdArray = array_unique(array_merge($assignUserIdArray, $executorUserIdArray));
                $users = model(UserModel::class)->where(["id" => ["in", implode(',', $userIdArray)]])->field("id,name,union_id")->select();
                $users = array_column($users, null, 'id');
                foreach ($outOfDateTask as $task) {
                    $outDateTaskIdArray[] = $task['id'];
                    $executor = $users[$task['executor']] ?? null;
                    $notifyParam = [
                        'task_name' => $task['entity_name'] . '-' . $task['name'],
                        'step_name' => $task['name'],
                        'plan_end_time' => $task['plan_end_time'],
                        'executor_name' => $executor['name'] ?? "",
                        'extra_data' => [
                            'task_id' => $task['id'],
                            'entity_id' => $task['entity_id'],
                        ],
                    ];

                    // 任务执行人
                    if (!empty($executor)) {
                        $detailMessage[$executor['union_id']]['out_date'][] = [
                            "link_type" => "task",
                            'link_id' => $task['id'],
                            'type' => 'out_date',
                            'message_content' => $notifyParam,
                        ];
                    }

                    // 任务分配人
                    $assignUser = $users[$task['assignee']] ?? null;
                    if (!empty($assignUser)) {
                        $detailMessage[$assignUser['union_id']]['out_date'][] = [
                            "link_type" => "task",
                            'link_id' => $task['id'],
                            'type' => 'out_date',
                            'message_content' => $notifyParam,
                        ];
                    }

                    // 父级任务分配人
                    $parentTask = $this->getParentTask($task['id'], $task['project_id']);
                    if ($parentTask) {
                        $parentTaskAssignUser = model(UserModel::class)->field("union_id")->cache(60)->find($parentTask['assignee']);
                        if (!empty($parentTaskAssignUser)) {
                            $detailMessage[$parentTaskAssignUser['union_id']]['out_date'][] = [
                                "link_type" => "task",
                                'link_id' => $task['id'],
                                'type' => 'out_date',
                                'message_content' => $notifyParam,
                            ];
                        }
                    }
                }
            }
        }

        $optionService = OptionsService::getInstance();
        $warningTagId = $optionService->getOptionsConfigItemData("task_tag_config", "warning_tag");

        // 标记预警任务
        if (!empty($warningTaskIdArray) && $warningTagId > 0) {
            $commonService = CommonService::instance("task");
            $commonService->setCurrentModuleCode("task");
            foreach ($warningTaskIdArray as $taskId) {

                $data = $commonService->dealUpdateCustomField([
                    'id' => $taskId,
                    'task_tag' => $warningTagId,
                ], 'task');
                model(TaskModel::class)->modifyItem($data);
            }
        }

        $outDateTagId = $optionService->getOptionsConfigItemData("task_tag_config", "out_date_tag");

        // 标记逾期任务
        if (!empty($outDateTaskIdArray) && $outDateTagId > 0) {
            $commonService = CommonService::instance("task");
            $commonService->setCurrentModuleCode("task");
            foreach ($outDateTaskIdArray as $taskId) {

                $data = $commonService->dealUpdateCustomField([
                    'id' => $taskId,
                    'task_tag' => $outDateTagId,
                ], 'task');
                model(TaskModel::class)->modifyItem($data);
            }
        }

        $count = 0;
        $templateId = $this->getTaskStatisticsDailyTemplateId();

        foreach ($detailMessage as $receiver => $messages) {

            $warningMessages = $messages['warning'] ?? [];
            $outDateMessages = $messages['out_date'] ?? [];
            $message['param'] = [
                'first' => "截止到" . date("Y-m-d H:i"),
                'remark' => "请点击查看",
                'keyword1' => count($warningMessages),
                'keyword2' => count($outDateMessages),
            ];
            $message['actions'] = [
                [
                    'key' => 'show_notify_message_detail',
                    'text' => "查看",
                    'param' => null,
                ],
            ];
            $detailData = array_merge($warningMessages, $outDateMessages);

            Client::send("send_notify_message", build_queue_data_with_xu([
                "param" => $message['param'],
                'receiver' => $receiver,
                'template_id' => $templateId,
                'content_type' => 'card_message',
                'actions' => $message['actions'],
                'notice_type' => 'message_task',
                'is_popup' => 1,
                'message_key' => 'task_statistics_daily',
                'extra_data' => [],
                'detail_data' => $detailData,
            ]));
            $count += 1;
        }

        return $count;
    }

    /**
     * 更新任务最后一个版本的打包名称
     * @param $data
     * @return array|bool
     * @throws \Exception
     */
    public function updateLatestVersionPackName($data)
    {
        $versionModel = model(VersionModel::class);
        // 查询任务的最后一个版本
        $version = $versionModel->find($data['id']);
        // 查询不到任务的版本
        if (empty($version)) {
            throw_strack_exception('version not found', ErrorCode::VERSION_NOT_FOUND);
        }
        // 更新任务打包名称

//        if (!empty($version['pack_name'])) {
//            // 不接受重复打包更新 @update 2021年5月8日
//            return [];
//        }
        $version = $versionModel->modifyItem([
            'id' => $version['id'],
            'pack_name' => $data['pack_name'],
            'pack_pass' => $data['pack_pass'] ?? "",
        ]);
        $task = $this->taskModel->find($version['link_id']);
        if (empty($task)) {
            throw_strack_exception("task {$version['link_id']} not found", ErrorCode::TASK_VERSION_NOT_FOUND);
        }
        $executor = model(UserModel::class)->field('union_id')->where(['id' => $task['executor']])->find();

        if (empty($executor)) {
            throw_strack_exception("user {$task['executor']} not found", ErrorCode::USER_EXECUTOR_NOT_FOUND);
        }

        // 更新订单文件传输列表 新增新版本
        Client::send('version_order_task_upstream_check', build_queue_data_with_xu(['version_id' => $version['id']]));

        $project = model(ProjectModel::class)->find($version['project_id']);
        teamones_request('teamones-im', 'oauth/send_data_to_user_channel', [
            'param' => [
                'filter' => [
                    'id' => $executor['union_id'],
                ],
                'data' => [
                    'event_key' => 'version_packaging_completion_notify',
                    'event_data' => [
                        'project_name' => $project['name'],
                        'version_id' => $version['id'],
                        'link_id' => $version['link_id'],
                        'pack_name' => $version['pack_name'],
                        'tenant_id' => $version['tenant_id'],
                    ],
                ],
            ],
        ]);
        return $version;
    }

    /**
     * 任务就绪
     * @param $taskId
     * @return array|bool|string
     * @throws \Exception
     */
    public function ready($taskId)
    {
        $task = $this->taskModel->find($taskId);
        if (empty($task)) {
            throw new LogicException("can`t found task", -201013);
        }
        $statusService = StatusService::getInstance();
        $readyStatus = $statusService->getTaskDefaultReadyStatus();
        if (!empty($readyStatus)) {
            $commonService = CommonService::instance("task");
            $dealData = $commonService->dealUpdateCustomField([
                'id' => $taskId,
                'task_status' => $readyStatus['id'],
            ]);
            $task = $this->taskModel->modifyItem($dealData);

            // 修改订单任务传输文件列表的解压状态
            $orderTaskFileListService = new OrderTaskFileListService();
            $orderTaskFileListService->markDownstreamTaskUnzipped($taskId);
        }
        return $task;
    }

    /**
     * 查询任务动态筛选条件
     * @param int $userId
     * @param int $unionId
     * @param int $tenantId
     * @param $param
     * @param $dataFilter
     * @param $moduleCode
     * @return array
     * @throws \Exception
     */
    public function getDynamicTaskListFilter(int $userId, int $unionId, int $tenantId, $param, $dataFilter, $moduleCode)
    {
        $filter = $param['param']['filter'] ?? [];
        $projectId = $filter['project_id'] ?? 0;
        unset($filter['project_id']);
        // 不进行租户id筛选   因为项目id就已经过滤了租户id
//        $filter['task.tenant_id'] = $tenantId;
        if ($projectId) {
            $projectService = ProjectService::getInstance();
            $subProjectIdList = $projectService->getProjectAllSubProject($projectId);
            if (!empty($subProjectIdList)) {
                $subProjectIdList[] = $projectId;
                $filter['task.project_id'] = ['IN', $subProjectIdList];
            } else {
                $filter['task.project_id'] = $projectId;
            }
        }
        $stepService = StepService::getInstance();
        if (in_array($moduleCode, ['asset', 'shot', 'sequence'])) {
            $fullStepCategoryList = $stepService->getStepCategoryByMergeModuleConfig($moduleCode, false);
            if (!empty($fullStepCategoryList)) {
                $filter[] = [
                    'task.step_category_id' => ['IN', implode(',', array_column($fullStepCategoryList, 'id'))],
                ];
            }
        } else {
            $fullStepCategoryList = $stepService->getShowStepCategory(false);
        }
        $waitReviewCount = [];
        switch ($dataFilter) {
            case 'leader_assign_list':
                /**
                 * 组装分配列表
                 * 使用任务分配人筛选
                 */
                $leaderAssignFilter = $filter;
                if (!empty($fullStepCategoryList)) {
                    $departmentList = TaskAssignService::getInstance()->getAssignUserDepartmentList();
                    $departmentIds = array_column($departmentList, 'id');
                    if (empty($departmentIds)) {
                        $fullStepCategoryList = [];
                    } else {
                        $leaderAssignFilter['task.assign_status'] = ['IN', [TaskModel::ASSIGN_STATUS_EXECUTOR]];
                        $leaderAssignFilter['task.step_type'] = 'step';
                        $leaderAssignFilter['task.department_id'] = ["IN", $departmentIds];
                        $statusData = array_column(model(StatusModel::class)->select(), 'id', 'code');
                        $leaderAssignFilter["json_extract(task.json,concat( '$.task_status' ))"] = ['NEQ', $statusData['closed']];
                        $myselfTaskStepCategoryIds = model(TaskModel::class)->where($leaderAssignFilter)->field('step_category_id as id,step_id')->group('step_category_id')
                            ->select();
                        $fullStepCategoryList = $myselfTaskStepCategoryIds;
                    }
                }
                break;
            case 'task_review_list':
                /**
                 * 任务审核列表 使用任务审核人进行筛选
                 */
                $reviewCountFilter = $filter;
                unset($reviewCountFilter['task.tenant_id']);
                // 进行审核申请租户id筛选
                $reviewCountFilter[] = [
                    'rrn.is_reviewed' => 'no',
                    'rr.tenant_id' => $tenantId,
                    'v.is_deprecate' => 'no',
                ];
                $waitReviewCount = model(TaskModel::class)
                    ->join('entity on entity.id = task.entity_id')
                    ->join('version v on v.link_id = task.id and v.module_id = ' . Module::$moduleDictData['module_index_by_code']['task']['id'])
                    ->join('review_request rr on rr.link_id = v.id and rr.module_id = ' . Module::$moduleDictData['module_index_by_code']['version']['id'])
                    ->join('review_request_node rrn on rrn.review_request_id = rr.id and rrn.plan_reviewer = ' . $userId)
                    ->where($reviewCountFilter)
                    ->field('count(*) as review_count,task.step_id')
                    ->group('task.step_id')
                    ->select();
                $waitReviewCount = array_column($waitReviewCount, 'review_count', 'step_id');
                break;
            case 'myself_task_list':
                /**
                 * 我的任务列表
                 */
                $myselfTaskFilter = $filter;
                if (!empty($fullStepCategoryList)) {
                    $myselfTaskFilter['task.executor_tenant_id'] = $tenantId;
                    $myselfTaskFilter['task.step_type'] = 'step';
                    $myselfTaskFilter['task.executor'] = $userId;
                    $myselfTaskStepCategoryIds = model(TaskModel::class)->where($myselfTaskFilter)->field('step_category_id as id,step_id')->group('step_category_id')->select();
                    $fullStepCategoryList = $myselfTaskStepCategoryIds;
                }
                break;
            default:
        }
        $optionService = OptionsService::getInstance();
        $statusList = $optionService->getTaskStatusList();
        $stepCategoryList = [];
        $resStepList = [];

        // 查询当前用户部门工序分类信息
//        $stepCategoryIdList = UCenterService::getInstance()->tenant->request('POST', 'tenant/get_user_department_step_id_list', [
//            'param' => [
//                'user_id' => $unionId,
//                'tenant_id' => $tenantId,
//            ]
//        ]);

        // 如果有模块筛选 需要和模块的分类做一次交集
        if (!empty($fullStepCategoryList)) {
            $stepCategoryIdList = array_column($fullStepCategoryList, 'id', 'id');
//            $fullStepCategoryIdList = array_column($fullStepCategoryList, 'id', 'id');
//            $stepCategoryIdList = array_intersect($fullStepCategoryIdList, $stepCategoryIdList);
        }

        if (!empty($stepCategoryIdList)) {
            $stepCategoryList = model(StepCategoryModel::class)->where(['id' => ['IN', $stepCategoryIdList]])
                ->field("concat('step_category',id) as id , id as real_id,name,code,'step_category' as type")
                ->order('index ASC')
                ->select();

            $stepList = model(StepModel::class)->where(['step_category_id' => ['IN', implode(',', $stepCategoryIdList)]])->field("concat('step',id) as id,id as real_id,name,code,'step' as type,step_category_id")->select();
            $stepList = array_group_by($stepList, 'step_category_id');
            $stepMap = array_column($stepList, null, 'real_id');
            foreach ($stepCategoryList as &$stepCategory) {
                $stepCategory ['wait_review_count'] = 0;
                $stepInfoList = $stepList[$stepCategory['real_id']] ?? [];
                foreach ($stepInfoList as $step) {
                    $step['wait_review_count'] = $stepMap[$step['real_id']]['wait_review_count'] = $waitReviewCount[$step['real_id']] ?? 0;
                    $stepCategory['wait_review_count'] += $step['wait_review_count'];
                    $stepCategory['children'][] = $step;
                    $step['id'] = $step['real_id'];
                    unset($step['real_id']);
                    $resStepList[] = $step;
                }
            }
        }

        return [
            'status_list' => $statusList,
            'step_category_list' => $stepCategoryList,
            'step_list' => $resStepList,
        ];
    }

    /**
     * 预警消息模板id
     * @return int
     */
//    private function getNotifyWarningTaskTemplateId()
//    {
//        $optionsService = new OptionsService();
//        $templateId = $optionsService->getOptionsConfigItemData('message_notify_template_config', 'notify_warning_task_template_id');
//        return (int)$templateId;
//    }

    /**
     * 逾期消息模板id
     * @return int
     */
//    private function getNotifyOutDateTaskTemplateId()
//    {
//        $optionsService = new OptionsService();
//        $templateId = $optionsService->getOptionsConfigItemData('message_notify_template_config', 'notify_out_date_task_template_id');
//        return (int)$templateId;
//    }

    /**
     * 任务更新
     * 前置任务是否完成检查
     * @param $param
     * @return array
     * @throws Exception
     */
    public function update($param)
    {
        $id = $param['data']['id'] ?? 0;
        $updateFields = $param['data'] ?? [];

        $task = model(TaskModel::class)->find($id);
        if (empty($task)) {
            throw_strack_exception("task {$id} not found", ErrorCode::TASK_NOT_FOUND);
        }

        // 检查前置任务的状态是否完成
        if ($task['pre_task_id'] > 0 && in_array('task_status', $updateFields)) {
            $preTask = model(TaskModel::class)->find($task['pre_task_id']);
            if (!empty($preTask)) {
                $extraFields = json_decode($preTask['json'], true);
                $statusService = StatusService::getInstance();
                $doneStatus = $statusService->getTaskDoneStatus();
                if ($extraFields['task_status'] != $doneStatus['id']) {
                    throw_strack_exception("Pre task not completed", ErrorCode::PRE_TASK_NOT_COMPLETED);
                }
            }
        }

        $commonService = CommonService::instance("task");
        return $commonService->commonUpdate($param);
    }

    /**
     * 任务日常统计模板id
     * @return int
     */
    private function getTaskStatisticsDailyTemplateId()
    {
        $templateId = OptionsService::getInstance()->getOptionsConfigItemData('message_notify_template_config', 'task_statistics_daily_template_id');
        return (int)$templateId;
    }

    /**
     * 检查任务能否启动
     * @param $taskId
     * @return bool
     * @throws Exception
     */
    public function taskCanStart($taskId)
    {
        $this->checkTaskAutoReady($taskId);
        $task = model(TaskModel::class)->find($taskId);
        $project = model(ProjectModel::class)->find($task['project_id']);
        if ($project['fast_workflow'] == "yes") {
            return true;
        }
        $statusService = StatusService::getInstance();
        $doneStatus = $statusService->getTaskDoneStatus();
        /**
         * 如果当前任务是有前置任务的
         * 那么只需要检查前置任务是否已完成
         */
        if ($task['pre_task_id']) {
            $preTask = model(TaskModel::class)
                ->field("id,JSON_UNQUOTE(JSON_EXTRACT(json,'$.task_status')) as task_status")
                ->find($task['pre_task_id']);

            if (empty($preTask)) {
                return true;
            }
            return $preTask['task_status'] == $doneStatus['id'];
        }

        $entity = model(EntityModel::class)->find($task['entity_id']);

        if (empty($entity)) {
            return true;
        }
        $workflow = model(StepWorkflowModel::class)->find($entity['step_workflow_id']);
        if (empty($workflow)) {
            throw new LogicException('workflow not found', ErrorCode::STEP_WORKFLOW_CONFIG_NOT_FOUND);
        }
        /**
         * 通过工序流 查找二级分类流关系
         */
        $stepCategorySorted = [];
        $config = json_decode($workflow['config'], true);
        foreach ($config['nodes'] as $nodeList) {
            foreach ($nodeList as $node) {
                if (in_array($node['step_category_id'], $stepCategorySorted)) {
                    continue;
                }
                $stepCategorySorted[] = $node['step_category_id'];
            }
        }
        $index = array_search($task['step_category_id'], $stepCategorySorted);

        /**
         * 在工序流里找不到当前任务的工序分类 就返回true
         */

        if ($index === false) {
            return true;
        } else if ($index > 0) {
            /**
             * 当前任务   跨二级分类
             */
            $preStepCategoryId = $stepCategorySorted[$index - 1];

            $latestPreStepCategoryStepTask = model(TaskModel::class)
                ->where([
                    'entity_id' => $entity['id'],
                    'step_category_id' => $preStepCategoryId,
                    'step_type' => 'step',
                ])
                ->field("id,JSON_UNQUOTE(JSON_EXTRACT(json,'$.task_status')) as task_status")
                ->order("pre_task_id DESC")
                ->find();

            if (empty($latestPreStepCategoryStepTask)) {
                return true;
            }
            return $latestPreStepCategoryStepTask['task_status'] == $doneStatus['id'];

        } else {
            /**
             * $index == 0
             * 当前任务   跨一级分类
             * 需要去拿一级分类流关系
             */
            $taskParentStepCategory = model(StepCategoryModel::class)->find($task['step_category_id']);
            $projectTemplate = model(ProjectTemplateModel::class)
                ->join("project ON project.project_template_id = project_template.id")
                ->where([
                    'project.id' => $task['project_id'],
                ])
                ->field("project_template.step_category_dependency")
                ->find();
            if (empty($projectTemplate)) {
                return true;
            }
            $stepCategoryDependency = json_decode($projectTemplate['step_category_dependency'], true);
            /**
             * 没有 一级分类关系配置 直接返回true
             */
            if (!isset($stepCategoryDependency['edges']) || empty($stepCategoryDependency['edges'])) {
                return true;
            }
            $sources = [];
            foreach ($stepCategoryDependency['edges'] as $edge) {
                if ($edge['target'] == $taskParentStepCategory['parent_id']) {
                    $sources[] = $edge['source'];
                }
            }
            if (empty($sources)) {
                return true;
            }
            /**
             * 上游
             */
            $sourceStepCategories = model(StepCategoryModel::class)
                ->where(['id' => ['IN', implode(',', $sources)]])
                ->select();

            $entityModule = Module::$moduleDictData['module_index_by_id'][$entity['module_id']];
            $entity['json'] = json_decode($entity['json'], true);
            $moduleCode = $entityModule['code'];
            $relatedEntities = [];
            foreach ($sourceStepCategories as $sourceStepCategory) {
                $relationEntityStr = $entity['json']["{$moduleCode}_{$sourceStepCategory['code']}"] ?? "";
                $tmpRelatedEntities = explode(',', $relationEntityStr);
                $tmpRelatedEntities = array_filter($tmpRelatedEntities, function ($item) {
                    return intval($item) > 0;
                });
                if (count($tmpRelatedEntities) == 0) {
                    continue;
                }
                $relatedEntities = array_merge($relatedEntities, $tmpRelatedEntities);
            }

            if (empty($relatedEntities)) {
                return true;
            }

            /**
             * 查看是否存在 关联实体没有完成的工序任务
             */
            $hasTaskNotDone = model(TaskModel::class)
                ->where([
                    "entity_id" => ['IN', $relatedEntities],
                    'step_type' => 'step',
                ])
                ->where("JSON_UNQUOTE(JSON_EXTRACT(json,'$.task_status')) != {$doneStatus['id']}")
                ->count();
            return $hasTaskNotDone == 0;
        }


    }

    /**
     * 工序任务工序标准查询
     * @param $param
     * @return mixed|null
     * @throws \Exception
     */
    public function stepTaskStandard($param)
    {
        $task = model(TaskModel::class)->where($param['filter'])->find();
        if (empty($task)) {
            return null;
        }
        $entity = model(EntityModel::class)->find($task['entity_id']);
        $entity = array_merge($entity, json_decode($entity['json'], true));
        unset($entity['json']);
        $project = model(ProjectModel::class)->find($task['project_id']);
        $workflow = model(StepWorkflowModel::class)->field('id,config')->find($entity['step_workflow_id']);
        $stepInfo = model(StepModel::class)->find($task['step_id']);
        $module = Module::$moduleDictData['module_index_by_id'][$entity['module_id']];

        $attributeFieldsMap = ModuleService::getInstance()->getModuleAttributeCodeFieldsMap($module['code']);
        $attributeFieldsMap = array_flip($attributeFieldsMap);
        $attributes = model(AttributeModel::class)->select();
        $attributeMap = array_column($attributes, null, 'code');

        $attrStr = '';
        try {
            $workflow['config'] = json_decode($workflow['config'], true);
            foreach ($workflow['config']['nodes'] as $nodeList) {
                foreach ($nodeList as $node) {
                    if ($node['module_code'] === $stepInfo['code']) {
                        if (count($node['config']) > 0) {
                            $akeys = array_column($node['config'], 'attribute_key');
                            sort($akeys);
                            foreach ($akeys as $key) {
                                $field = $attributeFieldsMap[$key] ?? null;
                                if (empty($field) || !array_key_exists($field, $entity)) {
                                    // 没有这个属性的  过滤掉
                                    continue;
                                }
                                $attrStr .= $attributeMap[$key]['name'] . ':' . $entity[$field] . '-';
                            }
                            $attrStr = trim($attrStr, '-');
                            break 2;
                        }
                    }
                }
            }
        } catch (Throwable $e) {
            trace(format_exception_info($e), 'ERR');
            $attrStr = '';
        }


        return DocumentRelationService::getInstance()->findDocument([
            "param" => [
                "filter" => [
                    "step_workflow_id" => $entity['step_workflow_id'],
                    "project_template_id" => $project['project_template_id'],
                    "step_id" => $task['step_id'],
                    'relation_type' => "task_standard",
                    'attr_str' => $attrStr,
                ],
            ],
        ]);
    }

    /**
     * 通过任务id找到项目模板
     * @param $taskId
     * @return array|int|mixed
     */
    public function getTaskProjectTemplateId($taskId)
    {
        $projectData = $this->taskModel->field('project_id')->where(['id' => $taskId])->find();
        if (empty($projectData)) {
            return [];
        }
        $projectTemplateData = model(ProjectModel::class)
            ->field('project_template_id,id')
            ->where(['id' => $projectData['project_id']])
            ->find();
        return $projectTemplateData['project_template_id'] ?? 0;
    }

    /**
     * 检查当前任务上个环节最后一个任务是否完成
     * @param $taskId
     * @return bool
     */
    public function isPreStepCategoryStepTaskDone($taskId): bool
    {
        $task = model(TaskModel::class)->find($taskId);
        $stepCategoryTaskList = model(TaskModel::class)
            ->where([
                'step_type' => 'step_category',
                'entity_id' => $task['entity_id'],
            ])
            ->field('id,step_category_id')
            ->order("id ASC")
            ->select();
        if (count($stepCategoryTaskList) <= 1) {
            return true;
        }

        // 检索上一个环节工序分类id
        $currentIndex = null;
        foreach ($stepCategoryTaskList as $key => $item) {
            if ($item['step_category_id'] == $task['step_category_id']) {
                $currentIndex = $key;
                break;
            }
        }
        // 如果没有查询到当前环节index  或者当前环节为第一个环节 那么直接返回 true
        if (is_null($currentIndex) && $currentIndex == 0) {
            return true;
        }
        // 查询到上个环节最后一个工序任务
        $preIndex = $currentIndex - 1;
        $stepCateGoryId = $stepCategoryTaskList[$preIndex]['step_category_id'];
        $latestPreStepCategoryStepTask = model(TaskModel::class)
            ->where([
                'entity_id' => $task['entity_id'],
                'step_category_id' => $stepCateGoryId,
                'step_type' => 'step',
            ])
            ->order('pre_task_id DESC')
            ->find();
        if (empty($latestPreStepCategoryStepTask)) {
            return true;
        }
        // 判断任务状态是否为已完成
        $latestPreStepCategoryStepTaskJson = json_decode($latestPreStepCategoryStepTask['json'], true);
        $taskStatus = $latestPreStepCategoryStepTaskJson['task_status'] ?? null;
        $statusService = StatusService::getInstance();
        $doneStatus = $statusService->getTaskDoneStatus();
        if (!empty($doneStatus)) {
            return $taskStatus == $doneStatus['id'];
        } else {
            return empty($taskStatus);
        }
    }

    /**
     * 检查任务自动ready状态
     * @param int $taskId
     * @throws Exception
     */
    public function checkTaskAutoReady(int $taskId)
    {
        $task = $this->taskModel->field("id,name,code,json,project_id,tenant_id,entity_module_id,JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.step_workflow_id')) as step_workflow_id")->find($taskId);
        if (empty($task)) {
            throw new LogicException('task not found', ErrorCode::TASK_NOT_FOUND);
        }
        // ready状态检查
        $statusService = StatusService::getInstance();
        $readyStatus = $statusService->getTaskDefaultReadyStatus();
        // 是否为默认状态
        $defaultStatus = $statusService->getTaskDefaultStatus();
        $taskStatus = json_decode($task['json'] ?? '{}', true)['task_status'] ?? null;
        if (empty($readyStatus) || $taskStatus != $defaultStatus['id']) {
            return;
        }

        // 概念任务/创意资产、特效资产 不需要组装,直接进入就绪状态
        if ($task['entity_module_id'] == module_by_code('design')['id']
            ||
            in_array($task['code'], ['efx_asset', 'creatives_asset'])
        ) {
            $taskUpdateData = [
                'id' => $taskId,
                'task_status' => $readyStatus['id'],
            ];
            CommonService::instance('task')->update($taskUpdateData, 'task', false);
            return;
        }
        // 如果设置了就绪状态 并且任务为默认状态 那么检查任务是否可以进入就绪
        $isReady = false;
        $upstreamTaskList = [];
        if (empty($task['step_workflow_id'])) {
            $upstreamTaskList = TaskUpstreamService::getInstance()->getUpstreamTaskList($task['id'], false, true);
        }
        if (!empty($task['step_workflow_id'])) {
            $upstreamTaskList = TaskUpstreamService::getInstance()->getUpstreamTaskListV2($task['id']);
        }
        if (empty($upstreamTaskList)) {
            $isReady = true;
        } else {
            // 检查是不是外包的影子项目
            $project = model(ProjectModel::class)->field('type')->find($task['project_id']);
            if ($project['type'] === 'outsource') {
                // 影子项目
                // 需要客户端下载文件 并且解压到工作文件夹 并且组装完成
                $upstreamTaskIdList = array_column($upstreamTaskList, 'id');
                $orderTaskUpstreamVersionListFilter = [
                    'task_id' => ['IN', $upstreamTaskIdList],
                    'to_tenant_id' => $task['tenant_id'],
                    'is_download' => 'yes',
                    'is_unzip' => 'yes',
                    'is_assemble' => 'yes',
                ];
                $orderTaskUpstreamVersionList = model(OrderTaskUpstreamVersionModel::class)->where($orderTaskUpstreamVersionListFilter)->group('task_id')->select();
                if (count($orderTaskUpstreamVersionList) === count($upstreamTaskList)) {
                    $isReady = true;
                }
            } else {
                // 需要检查任务的所有上游任务是否已经准备好
                $needAssembleTaskList = array_filter($upstreamTaskList, function ($item) {
                    return $item['is_assemble'] === 'no';
                });
                $isReady = count($needAssembleTaskList) === 0;
            }
        }
        APP_DEBUG && trace("task {$taskId} is ready " . var_export($isReady, true), 'DEBUG');
        if ($isReady) {
            // 更新为ready状态
            model(TaskModel::class)->execute("UPDATE `task` SET json = JSON_SET(IFNULL(json,'{}'),'$.task_status',{$readyStatus['id']}) where id = {$taskId}");
        }
    }

    /**
     * 获取当前点击的实体的工序流数据
     * @param $entityId
     * @param int $stepCategoryId
     * @param string $taskId
     * @return array
     */
    public function getEntityStepWorkflow($entityId, $stepCategoryId = 0, $taskId = '')
    {
        // 判断实体是否已经生成了任务
        $entityModel = model(EntityModel::class);
        $entity = $entityModel->where(["id" => $entityId])->field("is_generate,name")->find();
        $isGenerate = "no";
        $entityName = "";
        if (!empty($entity['name'])) {
            $entityName = $entity['name'];
        }

        // 如果未生成任务直接返回空值 []
        $taskList = [];
        $inProgressTask = [];

        if (!empty($entity['is_generate'])) {
            $isGenerate = $entity['is_generate'];
            if ($isGenerate === 'yes') {
                // 返回当前实体的任务列表
                $taskList = $this->getEntityStepTaskList($entityId, $stepCategoryId, $taskId);

                // 查找当前进行中的任务
                foreach ($taskList as $taskItem) {
                    $inProgressTask = $taskItem;
                    if ((int)$taskItem['end_time'] === 0) {
                        break;
                    }
                }
            }
        }

        return [
            "is_generate" => $isGenerate,
            "entity_name" => $entityName,
            "task_list" => $taskList,
            "current_inprogress_task" => $inProgressTask,
        ];
    }

    /**
     * 获取实体关联实体工序任务列表
     * @param int $entityId
     * @param int $taskId
     * @return array
     * @throws \Exception
     */
    public function getEntityRelationEntityStepTaskList(int $entityId, int $taskId)
    {
        $entityRelationData = [];

        $task = model(TaskModel::class)->field('id,project_id')->find($taskId);
        //查出外包实体
        $project = model(ProjectModel::class)->find($task['project_id']);
        if ($project['type'] == "outsource") {
            $outSideTask = OrderService::getInstance()->getSourceTaskByTaskId($task['id']);
            $entityId = $outSideTask['entity_id'] ?? '';
        }

        $entity = model(EntityModel::class)->find($entityId);

        if (empty($entity)) {
            return [];
        }

        $fieldService = FieldService::getInstance();
        $customFields = $fieldService->getEntityRelationEntityCustomFields($entity['module_id']);

        if (!empty($customFields)) {
            $entityModel = model(EntityModel::class);
            // 获取当前实体关联实体数据的ids 按照字段分类
            $filter = [];
            foreach ($customFields as $customField) {
                $filter[] = "JSON_UNQUOTE(JSON_EXTRACT(json, '$.{$customField}')) AS {$customField}";
            }
            $entityCustomData = $entityModel->field(join(",", $filter))->where(['id' => $entityId])->find();

            $entityRelationIds = [];
            foreach ($customFields as $customField) {
                if (array_key_exists($customField, $entityCustomData)) {
                    if (!empty($entityCustomData[$customField])) {
                        $tempEntityIds = explode(",", $entityCustomData[$customField]);
                        foreach ($tempEntityIds as $tempEntityId) {
                            if (!in_array($tempEntityId, $entityRelationIds)) {
                                $entityRelationIds[] = $tempEntityId;
                            }
                        }
                    }
                }
            }

            if (!empty($entityRelationIds)) {
                // 查找相关联实体信息
                $entityRelationData = $entityModel
                    ->join('module on entity.module_id = module.id')
                    ->field("entity.id,entity.name,entity.module_id,entity.initial_episode_code,entity.showings_number,
                    JSON_UNQUOTE(JSON_EXTRACT(entity.json,CONCAT('$.',module.`code`,'_category') )) AS category")
                    ->where([
                        'entity.id' => ['in', join(',', $entityRelationIds)],
                    ])
                    ->select();

                // 查找当前实体的最新进行中工序分类任务
                $taskModel = model(TaskModel::class);
                $taskDetailData = $taskModel->alias("task")
                    ->join("LEFT JOIN step_category ON step_category.id = task.step_category_id")
                    ->join("LEFT JOIN status ON status.id = JSON_UNQUOTE(JSON_EXTRACT(task.json, '$.task_status'))")
                    ->join("LEFT JOIN user ON user.id = task.executor")
                    ->where([
                        "task.entity_id" => ['in', join(",", $entityRelationIds)],
                        "step_type" => "step",
                    ])
                    ->field("
                        task.id as task_id,
                        task.name as task_name,
                        task.entity_id,
                        task.end_time as task_end_time,
                        step_category.id as step_category_id, 
                        step_category.name as step_category_name,
                        user.name as executor_name,
                        status.name as status_name
                    ")
                    ->order('task.entity_id asc')
                    ->select();

                $taskDetailDict = [];
                foreach ($taskDetailData as $taskDetailItem) {
                    if (array_key_exists($taskDetailItem['entity_id'], $taskDetailDict)) {
                        if ($taskDetailDict[$taskDetailItem['entity_id']]['task_end_time'] > 0) {
                            $taskDetailDict[$taskDetailItem['entity_id']] = $taskDetailItem;
                        }
                    } else {
                        $taskDetailDict[$taskDetailItem['entity_id']] = $taskDetailItem;
                    }
                }

                // 回插实体数据
                foreach ($entityRelationData as &$entityRelationItem) {
                    $entityRelationItem['task_id'] = $taskDetailDict[$entityRelationItem['id']]['task_id'] ?? 0;
                    $entityRelationItem['task_name'] = $taskDetailDict[$entityRelationItem['id']]['task_name'] ?? '';
                    $entityRelationItem['status_name'] = $taskDetailDict[$entityRelationItem['id']]['status_name'] ?? '';
                    $entityRelationItem['step_category_name'] = $taskDetailDict[$entityRelationItem['id']]['step_category_name'] ?? '';
                    $entityRelationItem['step_category_id'] = $taskDetailDict[$entityRelationItem['id']]['step_category_id'] ?? 0;
                    $entityRelationItem['module_name'] = Module::$moduleDictData['module_index_by_id'][$entityRelationItem['module_id']]['name'];
                }
            }
        }

        return $entityRelationData;
    }

    /**
     * 获取单个实体任务数据详情
     * @param $entityId
     * @param $taskId
     * @return array
     * @throws \Exception
     */
    public function getEntityStepTaskDetails($entityId, $taskId)
    {
        // 任务的基本信息
        $taskModel = model(TaskModel::class);

        $taskDetailData = $taskModel->alias("task")
            ->join("LEFT JOIN step ON step.id = task.step_id")
            ->join("LEFT JOIN status ON status.id = JSON_UNQUOTE(JSON_EXTRACT(task.json, '$.task_status'))")
            ->join("LEFT JOIN user ON user.id = task.executor")
            ->where([
                "task.id" => $taskId,
            ])
            ->field('
                task.id,task.name,task.code,task.tenant_id,task.entity_id,task.plan_end_time,task.end_time,task.executor_tenant_id,task.step_category_id,
                step.name as step_name,
                user.name as executor_name,
                status.name as status_name')->find();


        $entityRelationData = [];
        if (!empty($taskDetailData)) {
            $entity = model(EntityModel::class)->field("code,initial_episode_code,showings_number")->find($taskDetailData['entity_id']);
            $entityRelationData = $this->getEntityRelationEntityStepTaskList($entityId, $taskId);
            $taskDetailData['entity_code'] = $entity['code'];
            $taskDetailData['entity_initial_episode_code'] = $entity['initial_episode_code'];
            $taskDetailData['entity_showings_number'] = $entity['showings_number'];
            //当执行人没有,判断是否时外包公司的任务，则需要补充外包公司名字,否则填补空白数据
            if (empty($taskDetailData['executor_name'])) {
                $taskDetailData['executor_name'] = "";
                if ($taskDetailData['executor_tenant_id'] != 0 && $taskDetailData['executor_tenant_id'] != $taskDetailData['tenant_id']) {
                    // 筛选出外部供应商信息
                    $tenantInfo = teamones_request('teamones-im', 'tenant/find', ['param' => [
                        'filter' => [
                            'tenant.id' => $taskDetailData['executor_tenant_id'],
                        ],
                    ]])['data'];
                    //填入外包公司的名字
                    if (!empty($tenantInfo['name'])) {
                        $taskDetailData['executor_name'] = $tenantInfo['name'];
                    }
                }
            }

        } else {
            $taskDetailData = [];
        }


        // 关联关系, 实体之间的关联，得到自定义字段


        return [
            "details" => $taskDetailData,
            "entity_relation" => $entityRelationData,
        ];
    }

    /**
     * 获取多个实体二级任务数据详情
     * @param $entityTaskIdMappings
     * @return array
     */
    public function getEntityStepCategoryTask($entityTaskIdMappings)
    {

        if (!empty($entityTaskIdMappings)) {
            $taskIds = [];
            $entityIds = [];
            foreach ($entityTaskIdMappings as $entityTaskIdMapping) {
                if (!in_array($entityTaskIdMapping['task_id'], $taskIds)) {
                    $taskIds[] = $entityTaskIdMapping['task_id'];
                }
                if (!in_array($entityTaskIdMapping['entity_id'], $entityIds)) {
                    $entityIds[] = $entityTaskIdMapping['entity_id'];
                }
            }

            // 任务的基本信息
            $taskModel = model(TaskModel::class);
            $taskDetailData = $taskModel->alias("task")
                ->join("LEFT JOIN step_category ON step_category.id = task.step_category_id")
                ->join("LEFT JOIN status ON status.id = JSON_UNQUOTE(JSON_EXTRACT(task.json, '$.task_status'))")
                ->join("LEFT JOIN user ON user.id = task.executor")
                ->join("LEFT JOIN entity ON task.entity_id = entity.id")
                ->where([
                    "task.entity_id" => ['in', join(",", $entityIds)],
                    "task.id" => ['in', join(",", $taskIds)],
                    "step_type" => "step_category",
                ])
                ->field("
                task.id,
                entity.name,
                task.code,
                task.entity_id,
                step_category.name as step_category_name,
                user.name as executor_name,
                status.name as status_name,
                JSON_UNQUOTE(JSON_EXTRACT(entity.json, '$.asset_category')) AS asset_category
            ")->select();

            if (!empty($taskDetailData)) {
                foreach ($taskDetailData as &$taskDetailItem) {
                    $taskDetailItem['executor_name'] = $taskDetailItem['executor_name'] ?? "";
                    $taskDetailItem['asset_category'] = $taskDetailItem['asset_category'] ?? "";
                }
                return $taskDetailData;
            }
        }

        return [];
    }

    /**
     * 递归获取下游任务文件
     * @param int $taskId 任务id
     * @param bool $onlyStarted 是否只查询已开始的任务
     * @return array|false|mixed|string
     * @throws Exception
     */
    public function getDownstreamTaskListRecursion($taskId, $onlyStarted = false)
    {
        $taskList = TaskUpstreamService::getInstance()->getDownstreamTaskList($taskId, $onlyStarted);

        if (empty($taskList)) {
            return $taskList;
        }
        $currentTaskList = $taskList;
        foreach ($currentTaskList as $taskItem) {
            $tmpTaskList = $this->getDownstreamTaskListRecursion($taskItem['id'], $onlyStarted);
            if (!empty($tmpTaskList)) {
                $taskList = array_merge($taskList, $tmpTaskList);
            }
        }
        return $taskList;
    }

    /**
     * 分配任务给外部供应商
     * @param array $task
     * @param array $assignUser
     * @param array $executorUser
     * @param int $executorTenantId
     * @param int $planTime
     * @return array
     * @throws Exception
     * @throws Throwable
     */
    private function sendStepTaskToOutSideSupplier(array $task, array $assignUser, array $executorUser, int $executorTenantId, $planTime = 0): array
    {
        // 分配给外部供应商
        $project = model(ProjectModel::class)->find($task['project_id']);
        //外部供应商
        //处理影子项目和影子任务
        $shadowProjectId = model(ProjectModel::class)->where(['belong_project_id' => $project['id'], 'tenant_id' => $executorTenantId])->cache(60 * 3)->field("id")->find();

        if (empty($shadowProjectId)) {
            //克隆项目
            $project['created_by'] = $executorUser['id'];
            $shadowProjectId = ProjectService::getInstance()->shadowProject($project, $executorTenantId, $executorUser);
        } else {
            $shadowProjectId = $shadowProjectId['id'];
        }
        $res['shadow_project'] = [
            'id' => $shadowProjectId,
        ];


        //重置执行人 分配人 分配状态 分配制片 执行人租户id
        // 克隆工序任务
        // 这里会添加工序任务与工序任务的关联关系
        $task['executor'] = $task['assignee_producer'] = $task['executor_tenant_id'] = $task['assignee'] = 0;
        $task['is_assignee'] = 'no';

        $taskClone = $task;
        unset($taskClone['assign_status']);
        if ($taskClone["executor_type"] == "tenant_group") {
            $json = json_decode($taskClone['json'], true);
            unset($json["tenant_group_id"]);
            $taskClone["json"] = json_encode($json);
        }
        if ($taskClone['plan_delivery_time'] <= 0 && !empty($planTime)) {
            $taskClone['plan_delivery_time'] = $planTime;
        }
        $taskGenerateService = TaskGenerateService::getInstance();
        $shadowTask = OrderService::getInstance()->getOutsideTaskByTaskId($task['id'], ['`order`.to_tenant_id' => $executorTenantId]);
        if (empty($shadowTask)) {
            $shadowTask = $taskGenerateService->copyTask($taskClone, $shadowProjectId, $executorTenantId, 'shadow');
        } else {
            TaskAbolitionService::getInstance()->taskClose([$shadowTask], true, $assignUser['id'], 'assign_withdraw');
        }

        // 更新回sub类型
        if (!isset($shadowTask['type']) || $shadowTask['type'] != 'sub') {
            $this->taskModel->save([
                'id' => $shadowTask['id'],
                'type' => 'sub',
            ]);
        }
        $res['shadow_task'] = [
            'from' => $task,
            'to' => $shadowTask,
        ];

        /**
         * 处理分类任务
         * 1. 查询分类任务
         * 2. 检查分类任务是否已经克隆过
         * 3. 添加 外包项目工序分类任务和工序任务的关系
         */

        // 查询分类任务
        $parentTask = $this->getParentTask($task['id'], $task['project_id']);
        // 检查分类任务是否已经克隆过
        $shadowParentTask = $this->taskModel
            ->where([
                'entity_id' => $shadowTask['entity_id'],
                'step_category_id' => $shadowTask['step_category_id'],
                'step_type' => 'step_category',
            ])
            ->find();
        if (empty($shadowParentTask)) {
            if ($parentTask["executor_type"] == "tenant_group") {
                $parentJson = json_decode($parentTask['json'], true);
                unset($parentJson["tenant_group_id"]);
                $parentTask["json"] = json_encode($parentJson);
            }
            $shadowParentTask = $taskGenerateService->copyTask($parentTask, $shadowProjectId, $executorTenantId, 'shadow');
        }
        // 添加 外包项目工序分类任务和工序任务的关系
        model(TaskRelationModel::class)
            ->setSuffix(sprintf('_project_%d', $shadowTask['project_id']))
            ->addItem([
                'target_task_id' => $shadowTask['id'],
                'source_task_id' => $shadowParentTask['id'],
                'relation_type' => 'step_category',
            ]);

        /**
         *
         * 更新主项目的执行人
         *
         */
        // 更新执行人
        $changeExecutor = [
            'id' => $task['id'],
            'executor' => $executorUser['id'],
            'executor_tenant_id' => $executorTenantId,
            'is_assignee' => 'yes',
            'assignee_producer' => $assignUser['id'],
        ];
        if ($task['plan_delivery_time'] <= 0 && !empty($planTime)) {
            $changeExecutor['plan_delivery_time'] = $planTime;
        }
        $this->taskModel->save($changeExecutor);
        return $res;
    }

    /**
     * 制片直接分配工序任务给组长
     * 指派任务给组长
     * @param $taskId
     * @param $userUnionId
     * @param $assigneeUserId
     * @return array|false|string|null
     * @throws \Exception
     */
    private function setStepTaskAssignee($taskId, $userUnionId, $assigneeUserId)
    {

        $executorUserId = 0;
        if ($userUnionId > 0) {
            //执行人的用户信息
            $userService = UserService::getInstance();
            $executorUser = $userService->getUserByUnionId($userUnionId, true);
            if (empty($executorUser)) {
                throw new LogicException("can`t found user[{$userUnionId}]", ErrorCode::USER_NOT_FOUND);
            }
            $executorUserId = $executorUser['id'];
        }
        // 只有任务为默认状态的 才能继续分配给别人
        $defaultStatusFilterValue = $this->getNotStartStatusFilter();
        if (!empty($defaultStatusFilterValue)) {
            $defaultStatusConditionValue = " IN  (" . implode(',', $defaultStatusFilterValue) . ")";
        } else {
            $defaultStatusConditionValue = "IS NULL";
        }
        $task = $this->taskModel
            ->where("JSON_UNQUOTE(JSON_EXTRACT(json,'$.task_status')) {$defaultStatusConditionValue} AND id = {$taskId}")
            ->field("id,step_type,project_id,entity_id")
            ->find();
        if (empty($task)) {
            throw new LogicException("missing task [{$taskId}] or status is not [{$defaultStatusConditionValue}]", ErrorCode::STATUS_IS_NOT_DEFAULT_STATUS_VALUE);
        }

        //查出来的任务不是工序任务
        if ($task['step_type'] != "step") {
            throw new LogicException("[{$taskId}] is not an step task", ErrorCode::NOT_A_STEP_TASK);
        }

        // 分类任务设置执行制片
        $taskData = $this->taskModel->find($taskId);
        $projectId = $taskData['project_id'] ?? 0;
        $parentTask = $this->getParentTask($taskId, $projectId);
        if (!empty($parentTask)) {
            $this->taskModel->save([
                'id' => $parentTask['id'],
                'assignee' => $assigneeUserId,
                'is_assignee' => 'yes',
            ]);
        }
        // 更新任务分配人和分配制片

        $flag = $this->taskModel->save([
            'id' => $taskId,
            'assignee' => $executorUserId,
            'assignee_producer' => $assigneeUserId,
            'assignee_at' => time(),
            'assign_status' => 'wait_confirm',
        ]);
        if ($executorUserId > 0) {
            // 设置任务分配人之后 那么就标记实体已经分配
            model(EntityModel::class)->save(['id' => $task['entity_id'], 'is_assignee' => 'yes']);
        }

        if ($flag === false) {
            throw new LogicException("set task executor failure" . $this->taskModel->getError(), ErrorCode::SET_TASK_EXECUTOR_FAILURE);
        }
        return $this->taskModel->find($taskId);

    }

    /**
     * 查询任务需求与附件信息
     * @param $taskId
     * @return array
     */
    public function getSourceDesignRequirementsWithMedia($taskId)
    {
        /**
         * 查询任务的原始设计需求
         */
        $task = $this->taskModel->find($taskId);
        if (empty($task)) {
            throw new LogicException('task not found', ErrorCode::TASK_NOT_FOUND);
        }
        return EntityService::getInstance()->getSourceDesignRequirementsWithMedia($task['entity_id']);
    }

    /**
     * 获取指定任务id所属标准文档的唯一属性标识
     * @param int $taskId
     * @return string
     */
    public function getTaskStandardDocAttrStrById($taskId)
    {
        // 1、找到当前任务所属实体的 module_id
        $taskData = model(TaskModel::class)->where(['id' => $taskId])
            ->field('entity_id,entity_module_id,step_id')
            ->find();
        if (empty($taskData)) {
            // 任务id不存在
            throw new LogicException('task not found', ErrorCode::TASK_NOT_FOUND);
        }
        // 2、查找当前任务实体属性自定义字段映射数据
        $fieldService = FieldService::getInstance();
        $attributeRuleMapByCode = $fieldService->getEntityAttributeCustomFieldsMapping($taskData['entity_module_id'], 'code');
        // 3、查找当前实体所属的工序流id
        $entityData = model(EntityModel::class)->where(['id' => $taskData['entity_id']])->field('step_workflow_id,json')->find();
        if (!empty($entityData['json'])) {
            $entityData['json'] = json_decode($entityData['json'], true);
        }
        // 生成当前任务的工序流唯一属性标识
        return StepWorkflowService::getInstance()->generateWorkflowStepDocAttrStr($entityData['step_workflow_id'], $attributeRuleMapByCode, $entityData['json'], $taskData['step_id']);
    }

    /**
     * 查询供应商信息
     * @param $tenantId
     * @param array $insideSupplierUserId
     * @param array $outsideSupplierTenantId
     * @param array $departmentIdMap
     * @param array $executorUserId
     * @param array $tenantIdList
     * @param array $tenantGroupIds
     * @return array[]
     */
    public function queryTaskSupplierInfo($tenantId, $insideSupplierUserId = [], $outsideSupplierTenantId = [], $departmentIdMap = [], $executorUserId = [], $tenantIdList = [], $tenantGroupIds = [])
    {
        $supplierFilter = [];
        // 用户信息
        $userUnionMap = [];
        if (count($insideSupplierUserId) > 0) {
            $userData = model(UserModel::class)->where(['id' => ['in', implode(',', $insideSupplierUserId)]])->field('id,union_id')->select();
            $userUnionMap = array_column($userData, 'id', 'union_id');
            $supplierFilter[] = ['supplier.user_id' => ['-in', implode(',', array_column($userData, 'union_id'))]];
        }

        if (count($outsideSupplierTenantId)) {
            $supplierFilter[] = ['supplier.from_tenant_id', ['in', implode(',', $outsideSupplierTenantId)]];
        }
        $res = [
            'inside' => [],
            'outside' => [],
            'department' => [],
            'executor' => [],
            'tenant' => [],
            'tenant_group' => [],
        ];
        // 内部执行人查询
        if (count($executorUserId)) {
            $executorData = model(UserModel::class)->where(['id' => ['in', implode(',', $executorUserId)]])->field('id,name,email,phone,avatar')->select();
            $res['executor'] = array_column($executorData, null, 'id');
        }
        if (count($supplierFilter)) {
            if (count($supplierFilter) > 1) {
                $supplierFilter['_logic'] = '-or';
            }
            $supplierParam = ['param' => ['filter' => [$supplierFilter, 'supplier.tenant_id' => $tenantId,]]];
            $supplierList = teamones_request('teamones-im', 'supplier/select', $supplierParam)['data'];
            foreach ($supplierList['rows'] as &$row) {
                $row = [
                    'id' => $row['id'],
                    'from_tenant_id' => $row['from_tenant_id'],
                    'type' => $row['type'],
                    'user_name' => $row['user_name'],
                    'name' => $row['type'] === 'inside' ? $row['user_name'] : $row['from_tenant_name'],
                    'from_tenant_name' => $row['from_tenant_name'],
                    'department_name' => $row['department_name'],
                    'role_name' => $row['role_name'],
                    'saas_user_id' => $userUnionMap[$row['user_id']] ?? 0,
                ];
            }
            $res['inside'] = array_column(array_filter($supplierList['rows'], function ($item) {
                return $item['type'] === 'inside';
            }), null, 'saas_user_id');
            $res['outside'] = array_column(array_filter($supplierList['rows'], function ($item) {
                return $item['type'] === 'outside';
            }), null, 'from_tenant_id');
        }
        if (!empty($departmentIdMap)) {
            $departmentListFilter = [
                'param' => [
                    'filter' => [
                        'id' => ['-in', implode(',', $departmentIdMap)],
                    ],
                ],
            ];
            // todo 分页问题 可能会导致查不出来足够的部门信息
            $departmentList = teamones_request('teamones-im', 'department/base_select', $departmentListFilter)['data'];
            $res['department'] = array_column($departmentList, null, 'id');
        }

        //获取租户信息
        if (!empty($tenantIdList)) {
            $tenantData = teamones_request('teamones-im', 'tenant/select', ['param' => ['filter' => ['tenant.id' => [
                '-in', implode(',', $tenantIdList)]]]])['data'];
            $res['tenant'] = array_column($tenantData['rows'] ?? [], null, 'id');
        }

        //获取集团信息
        if (!empty($tenantGroupIds)) {
            $requestData = [
                "param" => [
                    "filter" => [
                        "id" => ["-in", join(",", $tenantGroupIds)],
                    ],
                ],
            ];
            $tenantGroupList = teamones_request('teamones-im', 'tenant_group/select', $requestData);
            $tenantGroupList = $tenantGroupList["data"]["rows"] ?? [];
            $res['tenant_group'] = array_column($tenantGroupList, null, 'id');
        }

        return $res;
    }

    /**
     * 查询任务关联的实体信息
     * @param int $taskId
     * @return array
     */
    public function getTaskRelationEntityList(int $taskId)
    {
        $task = $this->taskModel->find($taskId);
        if (empty($task)) {
            throw new LogicException('task not found', ErrorCode::TASK_NOT_FOUND);
        }
        $project = model(ProjectModel::class)->find($task['project_id']);
        if (empty($project)) {
            throw new LogicException('project not found', ErrorCode::PROJECT_NOT_FOUND);
        }
        if ($project['type'] === 'outsource') {
            $orderService = OrderService::getInstance();
            $partyATask = $orderService->getSourceTaskByTaskId($taskId);
            if (!empty($partyATask)) {
                $task = $partyATask;
            }
        }

        $res = [
            'current_entity' => null,
            'parent_entity' => null,
            'related_asset' => [],
            'related_level' => [],
            'entity_id_list' => [],
            'children_entity' => [],
        ];
        $entity = model(EntityModel::class)->field('id,entity_id,entity_module_id,module_id,json')->find($task['entity_id']);
        $res['current_entity'] = $entity['id'];
        $res['entity_id_list'][] = $entity['id'];

        $episodeModuleId = Module::$moduleDictData['module_index_by_code']['episode']['id'];
        if ($entity['entity_id'] > 0 && $entity['entity_module_id'] != $episodeModuleId) {
            $res['parent_entity'] = $entity['entity_id'];
            $res['entity_id_list'][] = $entity['entity_id'];
        }
        if ($entity['module_id'] === Module::$moduleDictData['module_index_by_code']['sequence']['id']) {
            $shotIdList = model(EntityModel::class)->where(['entity_id' => $entity['id']])->field('id')->select();
            $res['children_entity'] = array_column($shotIdList, 'id');
        }

        $relationFields = ['asset', 'level'];
        $entityJsonFields = json_decode($entity['json'], true);
        $currentEntityModule = Module::$moduleDictData['module_index_by_id'][$entity['module_id']];
        foreach ($relationFields as $field) {
            if (!empty($entityJsonFields[$currentEntityModule['code'] . '_' . $field]) && is_string($entityJsonFields[$currentEntityModule['code'] . '_' . $field])) {
                $relatedAssetList = explode(',', $entityJsonFields[$currentEntityModule['code'] . '_' . $field]);
                $res['related_' . $field] = array_filter($relatedAssetList, function ($item) use (&$res) {
                    if (intval($item) > 0) {
                        $res['entity_id_list'][] = $item;
                        return true;
                    }
                    return false;
                });
            }
        }

        return $res;
    }

    /**
     * 查询实体关联数据 任务,实体,上级实体
     * @param int $taskId
     * @param bool $closedShow
     * @return array[]
     */
    public function queryTaskRelationMap(int $taskId, $closedShow = false)
    {
        $task = $this->taskModel->find($taskId);
        // 子任务转换成父任务
        $task = DismantleTaskService::getInstance()->dismantleTaskTransformation($task);
        $entityRelationMap = $this->getTaskRelationEntityList($taskId);
        $projectTemplateId = $this->getTaskProjectTemplateId($task['id']);
        $taskJsonArray = json_decode($task['json'], true);

        $upstreamTaskList = [];
        $res = [
            'upstream_task' => [],
        ];
        $taskQueryFields = [
            'id',
            'name',
            'code',
            'man_hour',
            'actual_man_hour',
            'step_id',
            'step_category_id',
            'entity_id',
            'entity_module_id',
            'plan_start_time',
            'plan_end_time',
            'is_old_name',
            'task_status',
        ];
        if (empty($taskJsonArray['step_workflow_id'])) {
            $upstreamSteps = StepService::getInstance()->getStepUpstream($task['step_id'], $projectTemplateId, 0);
            $stepIdList = array_column($upstreamSteps, 'step_id', 'step_id');
            if (count($entityRelationMap['entity_id_list']) > 0 && count($stepIdList) > 0) {
                $upstreamTaskList = $this->taskModel
                    ->where(['step_id' => ['IN', $stepIdList], 'entity_id' => ['IN', $entityRelationMap['entity_id_list']]])
                    ->field(transform_custom_fields($taskQueryFields, 'task'))->select();
                if (!$closedShow) {
                    $closedStatus = StatusService::getInstance()->getOne(['code' => 'closed'], '*');
                    $closedStatusId = $closedStatus['id'];
                    $upstreamTaskList = array_filter($upstreamTaskList, function ($item) use ($closedStatusId) {
                        return empty($item['task_status']) || $item['task_status'] != $closedStatusId;
                    });
                    $upstreamTaskList = array_values($upstreamTaskList);
                }
            }
        }
        // 新工序流
        if (!empty($taskJsonArray['step_workflow_id'])) {
            $project = model(ProjectModel::class)->find($task['project_id']);
            if (empty($project)) {
                throw new LogicException('project not found', ErrorCode::PROJECT_NOT_FOUND);
            }
            if ($project['type'] === 'outsource') {
                $orderService = OrderService::getInstance();
                $partyATask = $orderService->getSourceTaskByTaskId($taskId);
                if (!empty($partyATask)) {
                    $taskId = $partyATask['id'];
                }
            }
            $upstreamList = TaskUpstreamService::getInstance()->getStepUpDownstreamTaskList($taskId);
            $newUpstreamTaskIdList = array_column($upstreamList, 'upstream_task_id');
            if (!empty($newUpstreamTaskIdList)) {
                $upstreamTaskList = $this->taskModel
                    ->where(['id' => ['IN', $newUpstreamTaskIdList]])
                    ->field(transform_custom_fields($taskQueryFields, 'task'))->select();
                if (!$closedShow) {
                    $closedStatus = StatusService::getInstance()->getOne(['code' => 'closed'], '*');
                    $closedStatusId = $closedStatus['id'];
                    $upstreamTaskList = array_filter($upstreamTaskList, function ($item) use ($closedStatusId) {
                        return empty($item['task_status']) || $item['task_status'] != $closedStatusId;
                    });
                    $upstreamTaskList = array_values($upstreamTaskList);
                }
            }
        }
        $stepList = $stepCategoryList = [];
        if (count($upstreamTaskList)) {
            $stepIdList = array_column($upstreamTaskList, 'step_id', 'step_id');
            $stepCategoryIdList = array_column($upstreamTaskList, 'step_category_id', 'step_category_id');
            $stepList = model(StepModel::class)->where(['id' => ['IN', $stepIdList]])->field('id,name,code')->select();
            $stepList = array_column($stepList, null, 'id');
            $stepCategoryList = model(StepCategoryModel::class)->where(['id' => ['IN', $stepCategoryIdList]])->field('id,name,code')->select();
            $stepCategoryList = array_column($stepCategoryList, null, 'id');
        }
        $entityQueryFields = [
            'id',
            'name',
            'code',
            'entity_id',
            'project_id',
            'entity_module_id',
            'initial_episode_code',
            'showings_number',
            'module_id',
            'json',
        ];

        $entityList = model(EntityModel::class)->where(['id' => ['IN', array_merge($entityRelationMap['entity_id_list'], $entityRelationMap['children_entity'])]])->field($entityQueryFields)->select();
        $entityList = array_column($entityList, null, 'id');

        $episodeCodeList = array_column($entityList, 'initial_episode_code', 'initial_episode_code');
        $currentEntity = $entityList[$entityRelationMap['current_entity']];
        $res['project'] = model(ProjectModel::class)->where(['id' => $currentEntity['project_id']])->field('id,name,code,type,work_dir,work_dir_prefix,engine_dir_prefix,directory_template')->find();
        CommonService::getFormatAttrData(model(ProjectModel::class), $res['project']);

        $episodeFilter = [
            'project_id' => $currentEntity['project_id'],
            'module_id' => Module::$moduleDictData['module_index_by_code']['episode']['id'],
            'code' => ['IN', $episodeCodeList],
        ];
        $episodeList = model(EntityModel::class)->where($episodeFilter)->field('id,name,code')->select();
        $episodeList = array_column($episodeList, null, 'code');
        $singleEntityId = [$entityRelationMap['parent_entity'], $entityRelationMap['current_entity']];
        $shotModuleId = Module::$moduleDictData['module_index_by_code']['shot']['id'];

        foreach ($entityList as &$entity) {
            $jsonFields = json_decode($entity['json'], true);
            $entity = $entity + $jsonFields;
            unset($entity['json']);
            $entity['episode'] = $episodeList[$entity['initial_episode_code']] ?? null;
//            $entity['project'] = $projectList[$entity['project_id']] ?? null;
            if ($entity['entity_module_id'] === $shotModuleId) {
                // 插入序列信息
                $var = $entityList[$entity['entity_id']] ?? null;
                $entity['sequence'] = &$var;
            }
            $currentModule = Module::$moduleDictData['module_index_by_id'][$entity['module_id']];
            // 是当前实体或者父级实体 写入对象数组
            if (in_array($entity['id'], $singleEntityId)) {
                $res[$currentModule['code']] = $entity;
                continue;
            }
            if (!isset($res[$currentModule['code']])) {
                $res[$currentModule['code']] = [];
            }
            // 如果是数组才写入
            if (!is_assoc($res[$currentModule['code']])) {
                $res[$currentModule['code']][] = $entity;
            }
        }

        foreach ($upstreamTaskList as &$upstreamTaskItem) {
            $upstreamTaskItem['entity'] = $entityList[$upstreamTaskItem['entity_id']] ?? null;
            $upstreamTaskItem['step'] = $stepList[$upstreamTaskItem['step_id']] ?? null;
            $upstreamTaskItem['step_category'] = $stepCategoryList[$upstreamTaskItem['step_category_id']] ?? null;
        }
        $res['upstream_task'] = $upstreamTaskList;
        return $res;
    }


    /**
     * 筛选出所有下游任务
     * @param $taskList
     * @param array $downStreamMap
     * @param $currentTask
     * @param array $downstreamTaskList
     */
    protected function findAllDownstreamTask($taskList, array $downStreamMap, $currentTask, array &$downstreamTaskList)
    {
        if (!empty($downStreamMap[$currentTask['step_id']])) {
            foreach ($downStreamMap[$currentTask['step_id']] as $downStepId) {
                if (empty($taskList[$downStepId])) {
                    continue;
                }
                foreach ($taskList[$downStepId] as $task) {
                    if (array_key_exists($task['id'], $downstreamTaskList)) {
                        continue;
                    }
                    $downstreamTaskList[$task['id']] = $task;
                    $this->findAllDownstreamTask($taskList, $downStreamMap, $task, $downstreamTaskList);
                }
            }
        }
    }

    /**
     * 更新任务审核状态
     * @param int $taskId
     * @param string $dealStatus
     * @param array $reviewRequest
     * @throws \Exception
     */
    public function updateTaskDealStatus(int $taskId, string $dealStatus, array $reviewRequest)
    {
        model(TaskModel::class)->where(['id' => $taskId])->save(['deal_status' => $dealStatus]);

        $plivList = model(PlaylistItemVersionModel::class)->where(['review_request_id' => $reviewRequest['id']])->field('id')->select();
        if (!empty($plivList)) {
            $plivIdList = array_column($plivList, 'id');
            model(PlaylistItemVersionModel::class)->where(['id' => ['IN', $plivIdList]])->save(['deal_status' => $dealStatus]);
        }
    }

    /**
     * 同步任务状态到外包源任务
     * @param int $outsideTaskId
     * @return int|null
     */
    public function syncTaskStatusToSourceTask(int $outsideTaskId)
    {
        $orderService = OrderService::getInstance();
        $task = model(TaskModel::class)->field("id,name,json->>'$.task_status' as task_status")->find($outsideTaskId);
        if (empty($task)) {
            return null;
        }
        $sourceTask = $orderService->getSourceTaskByTaskId($outsideTaskId);
        if (empty($sourceTask)) {
            return null;
        }
        $sourceTaskJsonFields = json_decode($sourceTask['json'] ?? '{}', true);
        $sourceTaskStatus = intval($sourceTaskJsonFields['task_status'] ?? 0);
        $outsideTaskStatus = intval($task['task_status']);
        if ($sourceTaskStatus === $outsideTaskStatus) {
            return null;
        }
        return $this->taskModel->execute("UPDATE task SET json=json_set(ifnull(json,'{}'),'$.task_status',{$outsideTaskStatus}) WHERE id = {$sourceTask['id']}");
    }

    /**
     * 获取已开始后的关联模型任务
     * @param $param
     * @return array|false|mixed|string
     */
    public function selectRelateModelTask($param)
    {
        // 找到对应编码的实体ID
        $entityIds = model(EntityModel::class)->where([$param['filter']])->column('id');
        $filter = [
            'step_category_id' => 3,
            'entity_id' => ['IN', $entityIds],
            '_string' => "json_extract(json,'$.task_status') not in (1,2,18)",
        ];
        // 未开始的范围：未开始，待组装，已就绪
        return model(TaskModel::class)->where($filter)->select();
    }

    /**
     * 设置任务过程文件
     * @param $data
     * @param $userId
     * @return array|bool|null
     * @throws Exception
     * @throws \Exception
     */
    public function setProcessFile($data, $userId)
    {
        $taskProcessFileService = CommonService::instance('task_process_file');
        //媒体处理
        $cosModel = new CosMediaModel();
        $cosMedia = $cosModel->addItem($data['cos_media']);
        if (!$cosMedia) {
            throw new \Exception($cosMedia->getError(), ErrorCode::MEDIA_ADD_FAILURE);
        }

        $versionData['task_process_file_cos_media'] = $cosMedia['id'];
        $versionData['file_name'] = $data['file_name'];
        $versionData['task_id'] = $data['task_id'];
        $versionData['version'] = $data['version'];
        $versionData['created_by'] = $userId;
        return $taskProcessFileService->commonCreate(['data' => $versionData], Module::$moduleDictData['module_index_by_code']['task_process_file']);
    }

    /**
     * 查询任务过程文件
     * @param $data
     * @return array
     * @throws Exception
     */
    public function getProcessFile($data)
    {
        $param['fields'] = $data['param']['fields'];
        $param['filter'] = $data['param']['filter'];
        $param['page'] = $data['param']['page'] ?? [1, C("database.database_max_select_rows")];
        $param['order'] = $data['param']['order'] ?? 'id desc';
        if ($data['param']['is_max_task_process_file']) {
            $fileId = $this->getMaxTaskProcessFileIds($data['param']['filter']['task_process_file.task_id']);
            $param['filter'] = ['task_process_file.id' => $fileId];
        }
        $commonService = CommonService::instance('task_process_file');
        $returnData = $commonService->select($param, 'task_process_file');
        if (empty($returnData)) {
            return [];
        }
        $createByIdList = [];
        foreach ($returnData['rows'] as $item) {
            if (empty($item['task_process_file']['created_by'])) {
                continue;
            }
            $createByIdList[] = $item['task_process_file']['created_by'];
        }
        if (!empty($createByIdList)) {
            $userInfo = TaskService::getInstance()->queryTaskSupplierInfo('', [], [], [], $createByIdList);
            $createByMap = $userInfo['executor'] ?? [];
            foreach ($returnData['rows'] as &$item) {
                $item['created_by'] = $createByMap[$item['task_process_file']['created_by']] ?? [];
            }
        }
        return $returnData;
    }

    /**
     * 获取最新的文件
     * @param $taskId
     * @return mixed|string
     */
    public function getMaxTaskProcessFileIds($taskId)
    {
        $maxVersion = model(TaskProcessFileModel::class)
            ->where(['task_id' => $taskId])
            ->field('max(id) as id')
            ->group('task_id')
            ->find();
        if (empty($maxVersion)) {
            return '';
        }
        return $maxVersion['id'];
    }


    /**
     * 获取更新时间任务树
     * @param \think\Request $request
     * @return \think\Response
     */
    public function getPopUpdateTimeTaskTree(Request $request)
    {
        $filter = $request->param()['param']['filter'];
        $resData = $this->shotService->getPopUpdateTimeTaskTree($filter['task_ids']);
        return json(success_response('', $resData));
    }

    /**
     * @param array $param
     * @return array|false|mixed|object|string|\think\Model|\think\model\RelationModel|null
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getJsonReferenceAsset(array $param)
    {
        $dbr = $this->taskModel->where(['id' => $param['task_id']])->field("id,name,code,project_id,json_unquote(json_extract(json, concat('$.','reference_asset'))) as reference_asset")->find();

        if (empty($dbr)) {
            return [];
        }

        $dbr['reference_asset'] = json_decode($dbr['reference_asset'], true);
        return $dbr;
    }


    /**
     * 检查子任务发布状态
     * @param int $taskId
     * @return bool
     */
    public function checkWhetherExistUnfinishedSubtask(int $taskId): bool
    {
        if (empty($taskId)) {
            return false;
        }
        $taskList = model(TaskRelationModel::class)
            ->alias('tr')
            ->join('task on task.id = tr.target_task_id')
            ->where(['tr.source_task_id' => $taskId])
            ->where(['tr.relation_type' => TaskRelationModel::RELATION_TYPE_DISMANTLE_TASK])
            ->field([
                "task.id",
                "JSON_UNQUOTE(JSON_EXTRACT(task.`json`, '$.task_status')) as task_status"
            ])
            ->select();
        if (empty($taskList)) {
            return false;
        }
        $statusPublishStatusIds = model(StatusModel::class)
            ->field("id")
            ->where(['correspond' => ['in', ['blocked', 'publish']]])
            ->column('id');
        foreach ($taskList as $val) {
            if (!in_array($val['task_status'], $statusPublishStatusIds)) {
                return true;
            }
        }
        return false;
    }


    /**
     * 更新任务工序流id
     * @param $updateEntityData
     * @return array
     * @throws \Exception
     */
    public function entityPublishedUpdateTaskStepWorkflowId($updateEntityData): array
    {
        if (empty($updateEntityData)) {
            return [];
        }
        $oldStepWorkflows = [];
        $stepWorkflowIds = array_column($updateEntityData, 'step_workflow_id');
        // 匹配是否为老工序流,如果配置从项目模板配置的,则为老工序流
        if (!empty($stepWorkflowIds)) {
            $oldStepWorkflows = model(StepWorkflowModel::class)
                ->where(['id' => ['IN', $stepWorkflowIds]])   ## 工序code
                ->where(['project_template_id' => ['GT', 0]]) ## 从项目模板配置的
                ->field('id')
                ->column('id');
        }
        $notClosedStatus = StatusService::getInstance()->byCodeMap(['closed'], true, 'id');
        foreach ($updateEntityData as $updateEntityItem) {
            if (empty($updateEntityItem['step_workflow_id'])) {
                continue;
            }
            if (empty($updateEntityItem['id'])) {
                continue;
            }
            if (empty($updateEntityItem['module_id'])) {
                continue;
            }
            if (empty($updateEntityItem['project_id'])) {
                continue;
            }
            $stepWorkflowId = $updateEntityItem['step_workflow_id'];
            // 如果是老工序流,不能新任务上的 step_workflow_id 字段
            if (!empty($oldStepWorkflows) && in_array($stepWorkflowId, $oldStepWorkflows)) {
                continue;
            }
            $entityId = $updateEntityItem['id'];
            $moduleId = $updateEntityItem['module_id'];
            $projectId = $updateEntityItem['project_id'];
            $stepType = TaskModel::STEP_TYPE_STEP;

            $updateSql = "update task set json = json_set(json, '$.step_workflow_id',$stepWorkflowId) where `entity_id` = {$entityId}";
            $updateSql .= " and entity_module_id = {$moduleId}";
            $updateSql .= " and project_id = {$projectId}";
            $updateSql .= " and step_type = '$stepType'";
            model(TaskModel::class)->execute($updateSql);

            // 同步更新外包实体工序流id
            $taskOutsideEntityList = model(TaskModel::class)
                ->join('order_task_list otl on otl.task_id = task.id and otl.status = "default"')
                ->join('task real_task on real_task.id = otl.real_task_id and real_task.json->>"$.task_status" in (' . $notClosedStatus . ')')
                ->where([
                    'task.entity_id' => $entityId,
                    'task.entity_module_id' => $moduleId,
                    'task.project_id' => $projectId,
                    'task.step_type' => $stepType,
                ])
                ->field('distinct real_task.entity_id')
                ->select();
            if (!empty($taskOutsideEntityList)) {
                $taskOutsideEntityList = array_column($taskOutsideEntityList, 'entity_id');
                $updateEntitySql = "update entity set step_workflow_id = " . $stepWorkflowId . " where id in (" . implode(',', $taskOutsideEntityList) . ")";
                model(EntityModel::class)->execute($updateEntitySql);
            }

        }
        return [];
    }

    /**
     * 获取任务使用的工序流
     * @param array $taskIds ["21344","4234324"]
     * @return array [{"task_id":"21344","step_workflow_id":"248"},{"task_id":"4234324","step_workflow_id":"0"}]
     */
    public function getTaskUseStepWorkflowList(array $taskIds): array
    {
        $useInfo = [];
        if (empty($taskIds) || count($taskIds) > 50) {
            return $useInfo;
        }
        // 通过外部任务id,查询到内部任务id
        $orderTaskIds = model(OrderTaskListModel::class)->where(['real_task_id' => ['IN', $taskIds]])->field(['task_id'])->column('task_id');
        // 内外部组装
        $taskIds = array_merge($taskIds, $orderTaskIds);
        $field = [
            'id',
            "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.step_workflow_id')) as step_workflow_id"
        ];
        $taskList = model(TaskModel::class)->where(['id' => ['IN', $taskIds]])->field($field)->select();
        if (empty($taskList)) {
            return $useInfo;
        }
        return $taskList;
    }

    /**
     * 获取每日交付率
     * @param $projectId
     * @param $deliveryTime
     * @return array ['delivery_task_num':"交付任务数","all_task_num":"所有任务数"]
     */
    public function getDailyDeliveryTaskList($projectId, $deliveryTime): array
    {
        if (empty($projectId) || $deliveryTime[0] > $deliveryTime[1]) {
            return [];
        }
        $fields = [
            'count(distinct if(delivery_status = 1, task_id, null)) as delivery_task_num',
            'count(distinct task_id)                                as all_task_num'
        ];
        return model(OriginalPlanNodeDetailModel::class)
            ->where(['project_id' => $projectId])
            ->where(['original_delivery_time' => ['BETWEEN', $deliveryTime]])
            ->field($fields)
            ->find();
    }
}
